处理仿射变换任务需获取两张图像的目标区域的三个坐标点((x11,y11),(x21,y21),(x31,y31)及(x12,y12),(x22,y22),(x32,y32)),三点确定一个平面,通过解6个方程获得6个参数。

        方程形式:

                x1=x2*a+y2*b+k1;

                y1=x2*c+y2*d+k2;

        通过opencv自带的 cv2.getAffineTransform()函数获得参数矩阵M,以及函数cv2.warpAffine()获得仿射变换后的图像。

测试

opencv 仿射变换还原 仿射变换c语言实现_仿射变换

ptsa.jpg

opencv 仿射变换还原 仿射变换c语言实现_opencv_02

ptsb.jpg

opencv 仿射变换还原 仿射变换c语言实现_ide_03

 res.jpg

        实现将ptsa两个三角形颜色替换为ptsb的颜色,保持ptsa的形状不变。在不获取ptsb的颜色的情况下,用仿射变换将ptsb中的形状直接变为ptsa,再将变换后的ptsb中的各坐标与ptsa对应并替换即可。

处理方法有2种:

1、将三角区域外接矩形放缩到同等大小,经过仿射变换后的图像目标区域可以直接替换到原图。

2、不进行放缩,直接仿射变换获得参数矩阵M(a,b,k1,c,d,k2),需要对目标区域所有坐标经M进行映射变换(上述方程)到原图。

方法一实现代码:

import cv2
import numpy as np

imga=cv2.imread('img/pica.jpg')
imgb=cv2.imread('img/picb.jpg')

ptsa=np.float32([[100,200],[250,100],[300,250]])#ptsa的左边三角区域三点坐标
ptsb=np.float32([[50,120],[150,50],[120,130]])#ptsb的左边三角区域三点坐标

ptsa_=np.float32([[250,100],[300,250],[400,150]])#ptsa的右边三角区域三点坐标
ptsb_=np.float32([[150,50],[120,130],[220,80]])#ptsb的右边三角区域三点坐标

# np.float32([[0,70*rh],[100*rw,0],[70*rw,80*rh]])

def IsTrangleOrArea(x1, y1, x2, y2, x3, y3):
    return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0)


def IsInside(x1, y1, x2, y2, x3, y3, x, y):
    # 三角形ABC的面积
    ABC = IsTrangleOrArea(x1, y1, x2, y2, x3, y3)

    # 三角形PBC的面积
    PBC = IsTrangleOrArea(x, y, x2, y2, x3, y3)

    # 三角形ABC的面积
    PAC = IsTrangleOrArea(x1, y1, x, y, x3, y3)

    # 三角形ABC的面积
    PAB = IsTrangleOrArea(x1, y1, x2, y2, x, y)

    return (ABC == PBC + PAC + PAB)


def warpTriangle(ptsa,ptsb,imga,imgb):
    r1=cv2.boundingRect(ptsa)
    r2=cv2.boundingRect(ptsb)
    roia=imga[r1[1]:r1[1]+r1[3],r1[0]:r1[0]+r1[2],:]
    roib=imgb[r2[1]:r2[1]+r2[3],r2[0]:r2[0]+r2[2],:]

    rate_h=roia.shape[0]/roib.shape[0]
    rate_w=roia.shape[1]/roib.shape[1]
    roib=cv2.resize(roib,(0,0),fx=rate_w,fy=rate_h)
    nptsa=[]
    nptsb=[]
    for i in range(3):
        nptsa.append([ptsa[i,0]-r1[0],ptsa[i,1]-r1[1]])
        nptsb.append([(ptsb[i, 0] - r2[0])*rate_w, (ptsb[i, 1] - r2[1])*rate_h])

    # print(ptsa)
    # print(ptsb)
    # print(nptsa)
    # print(nptsb)
    nptsa=np.array(nptsa,dtype=np.float32)
    nptsb=np.array(nptsb,dtype=np.float32)
    M=cv2.getAffineTransform(nptsb,nptsa)
    dst=cv2.warpAffine(roib,M,(roib.shape[1],roib.shape[0]))


    res=imga.copy()
    # print(ptsa[0,0], ptsa[0,1], ptsa[1,0], ptsa[1,1],ptsa[2,0], ptsa[2,1])
    print(r1[0],r1[0]+r1[2],r1[1], r1[1] + r1[3])
    for i in range(r1[1],r1[1]+r1[3]):
        for j in range(r1[0], r1[0] + r1[2]):
            if IsInside(ptsa[0,0], ptsa[0,1], ptsa[1,0], ptsa[1,1],ptsa[2,0], ptsa[2,1],j,i):
                res[i,j,:]=dst[i-r1[1],j-r1[0],:]

    return res


    # cv2.imshow('roia',roia)
    # cv2.imshow('dst',dst)


res=warpTriangle(ptsa,ptsb,imga,imgb)
res=warpTriangle(ptsa_,ptsb_,res,imgb)
cv2.imshow('res',res)
cv2.imshow('imgb',imgb)
cv2.imshow('imga', imga)
cv2.imwrite('res.jpg',res)
cv2.waitKey()

应用:

由于在处理图像过程中都是对矩形区域内像素进行遍历操作,而仿射变换需要3点坐标(即一个三角形),经变换后的图像部分区域不能直接替换到原图,而有效区域只是三角形内部,所以需要用到面积法进行有效区域的替换。如果处理的情况复杂,需要将目标区域拆分成多个三角区域(获取三点坐标)分别进行仿射变换。同时需要注意边界处理。