1.1.用途:
平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和剪切(Shear)
2.1.平移
说明:
点(x,y)向x方向移动dx, y方向移动dy
变换后坐标:
(x',y')=(x+dx,y+dy)
矩阵表示:
[x',y',1]=[1 0 dx][0 1 dy] [0 0 1]*[[x],[y],[1]]
2.2.缩放
说明:
设点(x,y)在x轴方向扩大sx倍,y轴方向扩大sy倍
变换后坐标:
(x',y')=(sx * x,sy * y)
矩阵表示:
[x',y',1]=[[sx 0 0] [0 sy 0][0 0 1]] *[[x],[y],[1]]#缩放矩阵
2.3.旋转
说明:
设点A(x1,y1)以原点(x0,y0)为圆心旋转θ度,求旋转后 A'(x',y')坐标值.
变换后坐标:
y = rows-y
x' = x0 + (x1 - x0) cosθ - (y1 - y0)sinθ
y' = y0 +(y1 - y0)cosθ + (x1 - x0) sinθ
y' = rows-y'
变换后坐标(x',y'):
x=rcos(b);y=rsin(b);
x'=rcos(a+b)=rcosacosb−rsinasinb=xcosa−ysina;(合角公式)
y'=rsin(a+b)=rsinacosb+rcosasinb=xsina+ycosa;
旋转变换矩阵:
[[cosa −sina][sina cosa]]#逆时针
2.4。翻转
[x',y',1]=[ [1 0 0 ][0 -1 N+1][0 0 1] ]*[x,y,1]
=============================================================================================
2.5.等距变换
R=[
[r11 r12 dx]#平移变换和旋转变换的复合;等距变换前后长度,面积,线段之间的夹角都不变
[r21 r22 dy]#左上角2×2矩阵为旋转部分,tx和ty为平移因子
[0 0 1 ]#有三自由度,即旋转,x方向平移,y方向平移。
]
=============================================================================================
2.6.切向变换
相当于一个横向截切与一个纵向剪切的复合常用于产生弹性物体的变形处理
[
[ 1 shx 0]
[shy 1 0]
[ 0 0 1]
]
#https://www.cnblogs.com/liekkas0626/p/5238564.html
=============================================================================================
2.7.相似变换
相似变换相当于是等距变换和均匀缩放的一个复合
相似变换前后长度比,夹角,虚圆点I,J保持不变。相似变换其实与相似三角形之间是有类似的
S=[
[s*r11 s*r12 dx]#左上角2×2矩阵为旋转部分,dx和dy为平移因子
[s*r21 s*r22 dy]#有4个自由度,即旋转,x方向平移,y方向平移和缩放因子s
[ 0 0 1 ]
]
=============================================================================================
2.8.仿射变换
性质:
1)保持二维图形“平直性”和“平行性”,面积比保持不变;
2)不具有保角性和保持距离比的性质不能保持垂直性角度会改变
共线线段或者平行线段的长度比保持不变,矢量的线性组合不变
3)有6自由度,即旋转4个,也就是前述大矩形的4个元素都可以同时改变,x方向平移,y方向平移。
包括平移、旋转、缩放、倾斜(错切、剪切、偏移)、翻转变换
说明:
平直性:变换后直线还是直线、圆弧还是圆弧。
平行性:平行线还是平行线,直线上点的位置顺序不变
计算:
[u,v,1]=[[a1 b1 c1] [a2 b2 c2 ][0 0 1]]*[x,y,1]
3.投影变换(透视变换,射影变换)
3.1.射影变换:
是最一般的线性变换。有8个自由度。
射影变换保持重合关系和交比不变。但不会保持平行性。即它会使得仿射变换产生非线性效应。
3.2.射影变换仿射变换区别:
射影变换变换矩阵[[a11 a12 a13] [a21 a22 a23 ][a31 a32 a33]]
仿射变换变换矩阵[[a11 a12 a13] [a21 a22 a23 ][0 0 1]]
在仿射的前提下,当左上角2×2矩阵正交时为欧式变换,左上角矩阵行列式为1时为定向欧式变换。
所以射影变换包含仿射变换,而仿射变换包含欧式变换。
[a11 a12 ] [a21 a22 ]表示线性变换如scaling(尺度),shearing(剪切)和ratotion(旋转)
[a13 a23]表示平移参数,一个确定在x方向上的平移一个确定在y方向上的平移;
[a31 a32]用于产生透视变换。从这里所以可以理解成仿射等是透视变换的特殊形式
名词解释:
projective transformation(投影变换) = homography(单应性变换) = collineation( 直射变换)
=============================================================================================
4.备注最终变换矩阵
2D基本模型视图变换只有上面这3种
如某变换先经过平移(对应平移矩阵A)再旋转(对应旋转矩阵B)再缩放(对应缩放矩阵C)
最终变换矩阵 T = ABC. #即3个矩阵按变换先后顺序依次相乘(矩阵乘法不满足交换律,讲究先后顺序)
5.实例:
旋转矩阵方法没有实现,有高手请补充
# !/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import cv2,math
class Pixel():
def __int__(self):pass
def __del__(self): pass
def movePixelPoint(self,x,y,dx=0,dy=0)->list:
b = [[1, 0, dx], [0, 1, dy], [0, 0, 1]]#平移矩阵
X = [[x], [y], [1]]
R=np.dot(b, X) # X'=bX
R=R.reshape(3)[0:2]
return R.tolist()
def zoomPixelPoint(self,x,y,sx=1,sy=1)->list:
b = np.array([[sx, 0, 0], [0, sy, 0], [0, 0, 1]],dtype=np.float)#缩放矩阵
X = np.array([[x], [y], [1]],dtype=np.float)
R = np.dot(b, X) # X'=bX
R = np.uint8(R.round())
R = R.reshape(3)[0:2]
return R.tolist()
def rotatePixelPoint(self,x,y,θ:int=0.0,center=(0.0,0.0),rows=480)->list:
cosθ=math.cos(math.radians(θ))
sinθ = math.sin(math.radians(θ))
x1 = x
y1 = rows - y
x0 = center[0]
y0 = rows - center[1]
x = (x1-x0)*cosθ - (y1 - y0)*sinθ + x0
y = (x1-x0)*sinθ + (y1 - y0)*cosθ + y0
x=x
y = rows - y
return x,y
def rotatePixelPoint_(self,x,y,angle:int=0.0,center=(0.0,0.0))->list:
scale=1
x0, y0 = center
cosθ = math.cos(math.radians(angle))
sinθ = math.sin(math.radians(angle))
α ,β= scale*cosθ,scale*sinθ
# b = [[ α,β,(1- α)*x0 - β*y0],[ -β,α,β*x0+(1 - α)*y0],[0, 0, 1]]
b =[[cosθ ,- sinθ,(1 - cosθ)*x0 + y0 * sinθ],[sinθ , cosθ,(1 - cosθ)*y0 - x0 * sinθ], [0 , 0 , 1]]
X = [[x], [y], [1]]
R = np.dot(b, X)
R = np.uint8(R.round())
R = R.reshape(3)[0:2]
R=R.tolist()
return R
#====================================================================================
def __move__(self,src, pos: '(int,int)') -> np.ndarray: # 彩色平移
rows, cols = src.shape[:2]
M = np.float32([[1, 0, pos[0]], [0, 1, pos[1]]]) # 平移转换矩阵: 右移动100;下移动50
dst = cv2.warpAffine(src, M, (cols, rows)) # 图像的宽度和高度
return dst
def __rotation_Scale__(self,src, angle:'float',rotation_center:'(float,float)'=None,scale:'float'=1)->np.ndarray:#彩色:旋转缩放
rows, cols = src.shape[:2]
cx=rotation_center[0] if rotation_center else cols/2.0
cy=rotation_center[1] if rotation_center else rows/2.0
# cols - 1 and rows - 1 are the coordinate limits.
M = cv2.getRotationMatrix2D((cx, cy), angle, scale)
dst = cv2.warpAffine(src, M, (int(2*cx), int(2*cy)))
return dst
def __polylines__(self,img,pts:'ndarray.int32', isClosed:'bool', color:'tuple',thickness=1,lineType=8)->np.ndarray:
tmp=thickness
if thickness<0:thickness=1
cv2.__polylines__(img, pts, isClosed, color, thickness, lineType)# 绘图多边形(pts多边形各定点int32, Isclosed是否封闭)
if tmp<0: cv2.fillPoly(img, pts, color)
return img
def __getColorPicture__(self):
img = np.zeros((480, 640, 3), dtype=np.uint8)
pts = np.array([[100, 50], [200, 300], [70, 200], [50, 100]], np.int32)
pts = pts.reshape((-1, 1, 2))
img = self.__polylines__(img, [pts], True, (125, 255, 0), -1)
cv2.putText(img, 'A', (100, 50-10), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255), 1, 8)
cv2.putText(img, 'B', (200, 300 + 10), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255), 1, 8)
cv2.putText(img, 'C', (70-20, 200+10), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255), 1, 8)
return img
def __getColorPictureInCenter__(self):
rgb=self.__getColorPicture__()
img,rect,box=self.__getContours__(rgb)
# 移动到中心
cx_rect, cy_rect = int(round(rect[0][0], 0)), int(round(rect[0][1], 0))
cv2.drawMarker(rgb, (cx_rect, cy_rect), (0, 0, 255))
h, w = int(round(rgb.shape[0] / 2, 0)), int(round(rgb.shape[1] / 2, 0))
dx, dy = w - cx_rect, h - cy_rect
rgb_center = self.__move__(rgb, (dx, dy))
return rgb_center
def __getContours__(self,rgb):
img=rgb.copy()
gray = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)
# 寻找二值图像轮廓
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[1]
cv2.drawContours(img, [cnt], 0, (125, 125, 125), 2)
# 获取最小外接矩形
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box.round())
cv2.drawContours(img, [box], 0, (0, 255, 255), 2)
return img,rect,box
def test_movePixelPoint(self,dx=50,dy=50):
rgb=self.__getColorPictureInCenter__()
img1,rect1,box1=self.__getContours__(rgb)
img_ = self.__move__(rgb, (dx, dy))
img2, rect2,box2 = self.__getContours__(img_)
for i in range(4):
x1,y1=box1[i]
x2,y2=box2[i]
x,y=self.movePixelPoint(x1,y1,dx,dy)
x_,y_=x1+dx,y1+dy
print('[%s %s %s %s ] [(x,y)=%s;(x1,y1)=%s;(x2,y2)=%s;]'%((x2-x1==dx),(y2-y1==dy),x2-x1-dx,y2-y1-dy,(x,y),(x1,y1),(x2,y2)))
cv2.imshow('s1', img1)
cv2.imshow('s2', img2)
cv2.waitKey()
def test_zoomPixelPoint(self,fx=0.5, fy=0.5):
rgb = self.__getColorPictureInCenter__()
img1, rect1, box1 = self.__getContours__(rgb)
img_ = cv2.resize(rgb,None,fx=fx, fy=fy, interpolation = cv2.INTER_CUBIC)
img2, rect2, box2 = self.__getContours__(img_)
for i in range(4):
x1, y1 = box1[i]
x2, y2 = box2[i]
x,y=self.zoomPixelPoint(x1, y1, sx=fx, sy=fy)
print('[%s %s %s %s ] [(x,y)=%s;(x1,y1)=%s;(x2,y2)=%s;]' % ((x2 - x == 0), (y2 - y == 0), x2 - x , y2 - y, (x, y), (x1, y1), (x2, y2)))
cv2.imshow('s1', img1)
cv2.imshow('s2', img2)
cv2.waitKey()
def test_rotatePixelPoint(self):
rotateangle = -180
center = (320, 240)
rgb = self.__getColorPictureInCenter__()
img1, rect1, box1 = self.__getContours__(rgb)
img_ = self.__rotation_Scale__(rgb, angle=rotateangle,rotation_center=center,scale=1)
img2, rect2, box2 = self.__getContours__(img_)
for i in range(4):
x1,y1=box1[i]
x2, y2 = box2[i]
x,y=self.rotatePixelPoint(x1, y1, rotateangle, center)
print('[%s %s %s %s ] [(x,y)=%s;(x1,y1)=%s;(x2,y2)=%s;]' % (
(x2 - x == 0), (y2 - y == 0), x2 - x, y2 - y, (x, y), (x1, y1), (x2, y2)))
cv2.imshow('s1', img1)
cv2.imshow('s2', img2)
cv2.waitKey()
if __name__ =='__main__':
a=Pixel()
# a.test_movePixelPoint(dx=150,dy=50)
"""
[True True 0 0 ] [(x,y)=(481, 432);(x1,y1)=(331, 382);(x2,y2)=(481, 432);]
[True True 0 0 ] [(x,y)=(380, 181);(x1,y1)=(230, 131);(x2,y2)=(380, 181);]
[True True 0 0 ] [(x,y)=(460, 149);(x1,y1)=(310, 99);(x2,y2)=(460, 149);]
[True True 0 0 ] [(x,y)=(561, 400);(x1,y1)=(411, 350);(x2,y2)=(561, 400);]
"""
# a.test_zoomPixelPoint()
"""
[False True -1 0 ] [(x,y)=(166, 191);(x1,y1)=(331, 382);(x2,y2)=(165, 191);]
[True False 0 -1 ] [(x,y)=(115, 66);(x1,y1)=(230, 131);(x2,y2)=(115, 65);]
[True False 0 -1 ] [(x,y)=(155, 50);(x1,y1)=(310, 99);(x2,y2)=(155, 49);]
[False False -1 -1 ] [(x,y)=(206, 175);(x1,y1)=(411, 350);(x2,y2)=(205, 174);]
"""
a.test_rotatePixelPoint()
"""
(x,y)=(309.0, 98.0) (x1,y1)=(331, 382) C (x2,y2)=(330, 381) A
(x,y)=(410.0, 349.0) (x1,y1)=(230, 131) D (x2,y2)=(229, 130) B
(x,y)=(330.0, 381.0) (x1,y1)=(310, 99) A (x2,y2)=(309, 98) C
(x,y)=(229.0, 130.0) (x1,y1)=(411, 350) B (x2,y2)=(410, 349) D
(x,y)=(330.0, 381.0) (x1,y1)=(310, 99) A (x2,y2)=(330, 381) A
(x,y)=(229.0, 130.0) (x1,y1)=(411, 350) B (x2,y2)=(229, 130) B
(x,y)=(309.0, 98.0) (x1,y1)=(331, 382) C (x2,y2)=(309, 98) C
(x,y)=(410.0, 349.0) (x1,y1)=(230, 131) D (x2,y2)=(410, 349) D
"""