实现我自己的算法以缩放和旋转python中的图像

时间:2018-09-10 01:24:20

标签: python opencv transformation

我正在尝试在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()

Image I am using for testing

1 个答案:

答案 0 :(得分:1)

您正在向后应用旋转。

这意味着对于20度的角度,您不是逆时针旋转20度,而是逆时针旋转20度。单独解决这个问题很容易-只需取消角度即可。

但这也意味着,对于每个目标像素,如果没有源像素精确地旋转到它,则最终会变成全黑像素。您可以使用任何插值算法来解决该问题,但这会使事情变得更加复杂。

如果我们只是颠倒该过程,而不是为每个(u, v)计算目标(x, y),我们为每个目标(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),但最终还是要与(原始版本)或循环(新版本)(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方阵,则可以将乘法矢量化。另外,您可以通过将移位矩阵@旋转矩阵@不移位矩阵相乘而不是在事后手动修复问题来更简单地创建矩阵。但是希望该版本尽可能接近您的原始版本,应该最容易理解。