簡體   English   中英

在python中實現我自己的算法來縮放和旋轉圖像

[英]Implementing my own algorithm to scale and rotate images in python

我正在嘗試在 python 中實現一種算法,以按比例縮放圖像或按給定角度(或同時)旋轉它們。 我正在使用 opencv 處理圖像,我知道 opencv 內置了這些功能,但是我想自己這樣做以更好地理解圖像轉換。 我相信我正確計算了旋轉矩陣。 但是,當我嘗試實現仿射變換時,它沒有正確出現。

import numpy as np 
import cv2
import math as m 
import sys

img = cv2.imread(sys.argv[1])
angle = sys.argv[2]

#get rotation matrix
def getRMat((cx, cy), angle, scale):
    a = scale*m.cos(angle*np.pi/180)
    b = scale*(m.sin(angle*np.pi/180))
    u = (1-a)*cx-b*cy
    v = b*cx+(1-a)*cy
    return np.array([[a,b,u], [-b,a,v]]) 

#determine shape of img
h, w = img.shape[:2]
#print h, w
#determine center of image
cx, cy = (w / 2, h / 2)

#calculate rotation matrix 
#then grab sine and cosine of the matrix
mat = getRMat((cx,cy), -int(angle), 1)
print mat
cos = np.abs(mat[0,0])
sin  = np.abs(mat[0,1])

#calculate new height and width to account for rotation
newWidth = int((h * sin) + (w * cos))
newHeight = int((h * cos) + (w * sin))
#print newWidth, newHeight

mat[0,2] += (newWidth / 2) - cx
mat[1,2] += (newHeight / 2) - cy

#this is how the image SHOULD look
dst = cv2.warpAffine(img, mat, (newWidth, newHeight))

cv2.imshow('dst', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

#apply transform
#attempt at my own warp affine function...still buggy tho
def warpAff(image, matrix, (width, height)):
    dst = np.zeros((width, height, 3), dtype=np.uint8)
    oldh, oldw = image.shape[:2]
    #print oldh, oldw
    #loop through old img and transform its coords
    for x in range(oldh):
        for y in range(oldw):
            #print y, x
            #transform the coordinates
            u = int(x*matrix[0,0]+y*matrix[0,1]+matrix[0,2])
            v = int(x*matrix[1,0]+y*matrix[1,1]+matrix[1,2])
            #print u, v
            #v -= width / 1.5
            if (u >= 0 and u < height) and (v >= 0 and v < width):
                dst[u,v] = image[x,y]
    return dst


dst = warpAff(img, mat, (newWidth, newHeight))

cv2.imshow('dst', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

我用於測試的圖像

您正在向后應用旋轉。

這意味着對於 20 度角,不是順時針旋轉 20 度,而是逆時針旋轉 20 度。 這本身很容易解決 - 只需否定角度。

但這也意味着,對於每個目標像素,如果沒有源像素完全旋轉到它,您最終會得到一個全黑像素。 您可以通過使用任何插值算法來解決該問題,但這會使事情變得更加復雜。

如果我們只是顛倒這個過程,而不是計算每個(x, y)的目的地(u, v) ,我們計算每個目的地(u, v)的源(x, y) (u, v) ,這解決了這兩個問題:

def warpAff(image, matrix, width, height):
    dst = np.zeros((width, height, 3), dtype=np.uint8)
    oldh, oldw = image.shape[:2]
    # Loop over the destination, not the source, to ensure that you cover
    # every destination pixel exactly 1 time, rather than 0-4 times.
    for u in range(width):
        for v in range(height):
            x = u*matrix[0,0]+v*matrix[0,1]+matrix[0,2]
            y = u*matrix[1,0]+v*matrix[1,1]+matrix[1,2]
            intx, inty = int(x), int(y)
            # We could interpolate here by using something like this linear
            # interpolation matrix, but let's keep it simple and not do that.
            # fracx, fracy = x%1, y%1
            # interp = np.array([[fracx*fracy, (1-fracx)*fracy],
            #                    [fracx*(1-fracy), (1-fracx)*(1-fracy)]])
            if 0 < x < oldw and 0 < y < oldh:
                dst[u, v] = image[intx, inty]
    return dst

現在唯一剩下的問題是你沒有向后應用移位,所以當我們轉動其他一切時,我們最終會在錯誤的方向上移動圖像。 解決這個問題很簡單:

mat[0,2] += cx - (newWidth / 2)
mat[1,2] += cy - (newHeight / 2)

你還有一個問題:你的代碼(和這個更新的代碼)只適用於方形圖像。 您多次向后獲取高度和寬度,並且它們幾乎全部抵消,但顯然其中之一沒有。 通常,您將數組視為(width, height)而不是(height, width) ,但最終會與 (original version) 進行比較或循環遍歷 (new version) (height, width) 因此,如果高度和寬度不同,您最終會嘗試寫入數組的末尾。

試圖找到所有這些並修復它們可能與從頭開始並從頭開始始終如一地做一樣多的工作:

mat = getRMat(cx, cy, int(angle), 1)
cos = np.abs(mat[0,0])
sin  = np.abs(mat[0,1])
newWidth = int((h * sin) + (w * cos))
newHeight = int((h * cos) + (w * sin))
mat[0,2] += cx - (newWidth / 2)
mat[1,2] += cy - (newHeight / 2)

def warpAff2(image, matrix, width, height):
    dst = np.zeros((height, width, 3), dtype=np.uint8)
    oldh, oldw = image.shape[:2]
    for u in range(width):
        for v in range(height):
            x = u*matrix[0,0]+v*matrix[0,1]+matrix[0,2]
            y = u*matrix[1,0]+v*matrix[1,1]+matrix[1,2]
            intx, inty = int(x), int(y)
            if 0 < intx < oldw and 0 < inty < oldh:
                pix = image[inty, intx]
                dst[v, u] = pix
    return dst

dst = warpAff2(img, mat, newWidth, newHeight)

值得注意的是,有更簡單(也更有效)的方法來實現這一點。 如果您構建一個 3x3 方陣,則可以對乘法進行矢量化。 此外,您可以更簡單地創建矩陣,只需將移位矩陣 @ 旋轉矩陣 @ 非移位矩陣相乘,而不是事后手動修復。 但希望這個版本,因為它盡可能接近你的原始版本,應該是最容易理解的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM