2013-01-05 27 views
29

पीआईएल के ट्रांसफॉर्म-फ़ंक्शन में परिप्रेक्ष्य-मोड होता है जिसके लिए 8-टुपल डेटा की आवश्यकता होती है लेकिन मैं यह समझ नहीं सकता कि कैसे परिवर्तित करना है, उस टुपल को 30 डिग्री का सही झुकाव कहें।परिप्रेक्ष्य परिवर्तन पीआईएल में कैसे काम करता है?

क्या कोई इसे समझा सकता है?

यहाँ यह करने के लिए प्रलेखन है: http://effbot.org/imagingbook/image.htm

+2

क्या आप परिप्रेक्ष्य में परिवर्तन समीकरणों से अवगत हैं? Http://xenia.media.mit.edu/~cwren/interpolator/ – mmgp

उत्तर

52

एक परिप्रेक्ष्य परिवर्तन आपको पहले एक विमान एक है कि उन बिंदुओं के साथ एक विमान बी में चार अंक के लिए मैप किया जाएगा में चार अंक जानने की आवश्यकता लागू करने के लिए, आप प्राप्त कर सकते हैं भौगोलिक परिवर्तन। ऐसा करके, आप अपने 8 गुणांक प्राप्त करते हैं और परिवर्तन हो सकता है।

साइट http://xenia.media.mit.edu/~cwren/interpolator/, साथ ही कई अन्य ग्रंथों का वर्णन करता है कि उन गुणांकों को कैसे निर्धारित किया जा सकता है। आपकी सुविधा के लिए, यहाँ उल्लेख लिंक से अनुसार एक सीधा दिया गया है:

import numpy 

def find_coeffs(pa, pb): 
    matrix = [] 
    for p1, p2 in zip(pa, pb): 
     matrix.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]]) 
     matrix.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]]) 

    A = numpy.matrix(matrix, dtype=numpy.float) 
    B = numpy.array(pb).reshape(8) 

    res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B) 
    return numpy.array(res).reshape(8) 

जहां pb वर्तमान विमान में चार कोने है, और जिसके परिणामस्वरूप pa विमान में चार कोने में शामिल है।

तो, लगता है हम में रूप में एक छवि को बदलने:

import sys 
from PIL import Image 

img = Image.open(sys.argv[1]) 
width, height = img.size 
m = -0.5 
xshift = abs(m) * width 
new_width = width + int(round(xshift)) 
img = img.transform((new_width, height), Image.AFFINE, 
     (1, m, -xshift if m > 0 else 0, 0, 1, 0), Image.BICUBIC) 
img.save(sys.argv[2]) 

यहां नमूने के इनपुट और इसके बाद के संस्करण कोड के साथ उत्पादन होता है:

enter image description hereenter image description here

हम पिछले कोड पर जारी रख सकते हैं और कतरनी को वापस करने के लिए एक परिप्रेक्ष्य परिवर्तन करें:

coeffs = find_coeffs(
     [(0, 0), (256, 0), (256, 256), (0, 256)], 
     [(0, 0), (256, 0), (new_width, height), (xshift, height)]) 

img.transform((width, height), Image.PERSPECTIVE, coeffs, 
     Image.BICUBIC).save(sys.argv[3]) 

में परिणामी:

enter image description hereenter image description here

+1

देखें आपका उत्तर बहुत उपयोगी और स्पष्ट है। धन्यवाद। क्या आप 'def find_coeffs (pa, pb)' के किसी भी शुद्ध-पायथन कार्यान्वयन से अवगत हैं? मैं अपने सिस्टम के एक गैर-केंद्रीय भाग के लिए एक numpy निर्भरता जोड़ने से बचने की उम्मीद कर रहा हूँ। मुझे लगता है कि मैं इसे खुद से बाहर कर सकता हूं लेकिन मुझे उम्मीद है कि यह कहीं पहले से बाहर है। – KobeJohn

+1

आपकी विशेष परियोजना @ कोबेजोहन के लिए बहुत देर हो सकती है, लेकिन मैंने अभी एक नया उत्तर पोस्ट किया है जिसमें गुणांक उत्पन्न करने के लिए शुद्ध-पायथन समाधान है। –

+2

@mmgp आपके उत्तर में प्रदान किया गया लिंक अब टूट गया है, 403 दे रहा है। – bcdan

7

मैं इस सवाल का अपहरण करने की सिर्फ एक छोटा सा क्योंकि जा रहा हूँ:

enter image description here

तुम भी गंतव्य अंक के साथ कुछ मजा कर सकते हैं पाइथन में परिप्रेक्ष्य परिवर्तन से संबंधित Google पर यह एकमात्र चीज है।

import numpy as np 

def create_perspective_transform_matrix(src, dst): 
    """ Creates a perspective transformation matrix which transforms points 
     in quadrilateral ``src`` to the corresponding points on quadrilateral 
     ``dst``. 

     Will raise a ``np.linalg.LinAlgError`` on invalid input. 
     """ 
    # See: 
    # * http://xenia.media.mit.edu/~cwren/interpolator/ 
    # * http://stackoverflow.com/a/14178717/71522 
    in_matrix = [] 
    for (x, y), (X, Y) in zip(src, dst): 
     in_matrix.extend([ 
      [x, y, 1, 0, 0, 0, -X * x, -X * y], 
      [0, 0, 0, x, y, 1, -Y * x, -Y * y], 
     ]) 

    A = np.matrix(in_matrix, dtype=np.float) 
    B = np.array(dst).reshape(8) 
    af = np.dot(np.linalg.inv(A.T * A) * A.T, B) 
    return np.append(np.array(af).reshape(8), 1).reshape((3, 3)) 


def create_perspective_transform(src, dst, round=False, splat_args=False): 
    """ Returns a function which will transform points in quadrilateral 
     ``src`` to the corresponding points on quadrilateral ``dst``:: 

      >>> transform = create_perspective_transform(
      ...  [(0, 0), (10, 0), (10, 10), (0, 10)], 
      ...  [(50, 50), (100, 50), (100, 100), (50, 100)], 
      ...) 
      >>> transform((5, 5)) 
      (74.99999999999639, 74.999999999999957) 

     If ``round`` is ``True`` then points will be rounded to the nearest 
     integer and integer values will be returned. 

      >>> transform = create_perspective_transform(
      ...  [(0, 0), (10, 0), (10, 10), (0, 10)], 
      ...  [(50, 50), (100, 50), (100, 100), (50, 100)], 
      ...  round=True, 
      ...) 
      >>> transform((5, 5)) 
      (75, 75) 

     If ``splat_args`` is ``True`` the function will accept two arguments 
     instead of a tuple. 

      >>> transform = create_perspective_transform(
      ...  [(0, 0), (10, 0), (10, 10), (0, 10)], 
      ...  [(50, 50), (100, 50), (100, 100), (50, 100)], 
      ...  splat_args=True, 
      ...) 
      >>> transform(5, 5) 
      (74.99999999999639, 74.999999999999957) 

     If the input values yield an invalid transformation matrix an identity 
     function will be returned and the ``error`` attribute will be set to a 
     description of the error:: 

      >>> tranform = create_perspective_transform(
      ...  np.zeros((4, 2)), 
      ...  np.zeros((4, 2)), 
      ...) 
      >>> transform((5, 5)) 
      (5.0, 5.0) 
      >>> transform.error 
      'invalid input quads (...): Singular matrix 
     """ 
    try: 
     transform_matrix = create_perspective_transform_matrix(src, dst) 
     error = None 
    except np.linalg.LinAlgError as e: 
     transform_matrix = np.identity(3, dtype=np.float) 
     error = "invalid input quads (%s and %s): %s" %(src, dst, e) 
     error = error.replace("\n", "") 

    to_eval = "def perspective_transform(%s):\n" %(
     splat_args and "*pt" or "pt", 
    ) 
    to_eval += " res = np.dot(transform_matrix, ((pt[0],), (pt[1],), (1,)))\n" 
    to_eval += " res = res/res[2]\n" 
    if round: 
     to_eval += " return (int(round(res[0][0])), int(round(res[1][0])))\n" 
    else: 
     to_eval += " return (res[0][0], res[1][0])\n" 
    locals = { 
     "transform_matrix": transform_matrix, 
    } 
    locals.update(globals()) 
    exec to_eval in locals, locals 
    res = locals["perspective_transform"] 
    res.matrix = transform_matrix 
    res.error = error 
    return res 
4

यहाँ एक शुद्ध अजगर को बदलने पैदा करने की संस्करण है: यहाँ कुछ थोड़ा और अधिक सामान्य कोड ऊपर के आधार पर जो एक परिप्रेक्ष्य मैट्रिक्स को बदलने बनाता है और एक समारोह जो कि मनमाने ढंग से अंक पर बदलने चलेंगे उत्पन्न करता है गुणांक (जैसा कि मैंने इसे कई लोगों द्वारा अनुरोध किया है)। मैंने PyDraw शुद्ध-पायथन छवि ड्राइंग पैकेज बनाने के लिए इसे बनाया और इस्तेमाल किया।

यदि यह अपनी परियोजना के लिए उपयोग कर, ध्यान दें कि गणना कई उन्नत मैट्रिक्स आपरेशन जिसका अर्थ है कि यह समारोह एक और की आवश्यकता की आवश्यकता है, सौभाग्य से शुद्ध अजगर, मैट्रिक्स पुस्तकालय matfunc मूल रूप रेमंड Hettinger द्वारा लिखित कहा जाता है और जो आप कर सकते हैं download here या here

एक्स '= (एक x + b y +:

import matfunc as mt 

def perspective_coefficients(self, oldplane, newplane): 
    """ 
    Calculates and returns the transform coefficients needed for a perspective 
    transform, ie tilting an image in 3D. 
    Note: it is not very obvious how to set the oldplane and newplane arguments 
    in order to tilt an image the way one wants. Need to make the arguments more 
    user-friendly and handle the oldplane/newplane behind the scenes. 
    Some hints on how to do that at http://www.cs.utexas.edu/~fussell/courses/cs384g/lectures/lecture20-Z_buffer_pipeline.pdf 

    | **option** | **description** 
    | --- | --- 
    | oldplane | a list of four old xy coordinate pairs 
    | newplane | four points in the new plane corresponding to the old points 

    """ 
    # first find the transform coefficients, thanks to http://stackoverflow.com/questions/14177744/how-does-perspective-transformation-work-in-pil 
    pb,pa = oldplane,newplane 
    grid = [] 
    for p1,p2 in zip(pa, pb): 
     grid.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]]) 
     grid.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]]) 

    # then do some matrix magic 
    A = mt.Matrix(grid) 
    B = mt.Vec([xory for xy in pb for xory in xy]) 
    AT = A.tr() 
    ATA = AT.mmul(A) 
    gridinv = ATA.inverse() 
    invAT = gridinv.mmul(AT) 
    res = invAT.mmul(B) 
    a,b,c,d,e,f,g,h = res.flatten() 

    # finito 
    return a,b,c,d,e,f,g,h 
+0

यह सुंदर है! मेरे लिए एक संदेश छोड़ने के लिए धन्यवाद। मैंने अपने ऐप को केवल मूलभूत एफ़िन गुणांक की गणना करने के तरीके के बारे में पता लगाया लेकिन मैं भविष्य में इसके लिए वापस आ सकता हूं और अधिक जटिल परिवर्तनों के लिए इसका उपयोग कर सकता हूं। – KobeJohn

2

8 को बदलने गुणांक (ए, बी, सी, डी, ई, एफ, जी, एच) के बाद परिवर्तन के अनुरूप ग)/(छ x + ज y + 1)
वाई '= (घ x + ई y + च)/(छ x + ज y + 1)

ये 8 गुणांक कर सकते हैं सामान्य रूप से 8 (रैखिक) को हल करने से मिलता है) समीकरण जो विमान परिवर्तन पर 4 अंक (2 डी -> 8 समीकरणों में 4 अंक) को परिभाषित करते हैं, इसे हल करने वाले कोड के लिए mmgp द्वारा उत्तर देखें, हालांकि आपको

को बदलने के लिए यह अधिक सटीक हो सकता है
res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B) 

res = numpy.linalg.solve(A, B) 

यानी करने के लिए, वहाँ कोई वास्तविक कारण वास्तव में वहाँ एक मैट्रिक्स को उलटने के लिए, या अपने पक्षांतरित और सटीकता का एक सा खोने से गुणा करने के लिए, समीकरणों को हल करने के क्रम में है।

अपने प्रश्न के लिए के रूप में, चारों ओर थीटा डिग्री की एक सरल झुकाव (x0, Y0) के लिए, गुणांक आप देख रहे हैं कर रहे हैं:

def find_rotation_coeffs(theta, x0, y0): 
    ct = cos(theta) 
    st = sin(theta) 
    return np.array([ct, -st, x0*(1-ct) + y0*st, st, ct, y0*(1-ct)-x0*st,0,0]) 

और सामान्य किसी भी Affine परिवर्तन में (छ, ज होना आवश्यक है) शून्य के बराबर। उम्मीद है की वो मदद करदे!