【python】图像映射:单应性变换与图像扭曲

【python】图像映射:单应性变换与图像扭曲

单应性变换(Homography)

单应性变换(Homography)即一个平面中的点到另一个平面的映射关系,如下图所示,使用单应性矩阵,将不同角度拍摄的图像图1和图2变换到同一平面。
在这里插入图片描述
而实现单应性变化的重点就是对单应性矩阵H的求解。如下图所示,其中x1y1表示图像一的矩阵数据,x2y2表示图像二的矩阵数据,而H则为单应性矩阵,通过求解H即可得出两图像间的映射关系。
在这里插入图片描述
在一般情况下,矩阵A不可逆,若需要求得t的值(下图中t即为单应性矩阵)则需要做一定的数学变换
在这里插入图片描述
如下公式所示,进行转换后,通过计算最小二乘解求得最小特征值对应的特征向量
在这里插入图片描述
再通过DLT(Direct Linear Transform)算法解决该最小二乘问题,如下图,通过DTL算法得A = UDVt, 而h (单应性矩阵)即为V的最后一行
在这里插入图片描述
单应性变换的结果诶下图,图中将图1与图2通过单应性变换进行了图片融合在这里插入图片描述

图像扭曲(仿射变换)

图像扭曲即仿射变换。仿射变换变化包括缩放、平移、旋转、反射等,,原来的直线仿射变换后还是直线,原来的平行线经过仿射变换之后还是平行线。
仿射变换中一些性质会保持不变:
1.凸性
2.共线性:若几个点变换前在一条线上,则仿射变换后仍然在一条线上
3.平行性:若两条线变换前平行,则变换后仍然平行
4.共线比例不变性:变换前一条线上两条线段的比例,在变换后比例不变

在这里插入图片描述

from numpy import *
from matplotlib.pyplot import *
from scipy import ndimage
from PIL import Image

im = array(Image.open('11.jpg').convert('L'))
H = array([[1.4,0.05,-100],[0.05,1.5,-100],[0,0,1]])
im2 = ndimage.affine_transform(im, H[:2,:2],(H[0,2],H[1,2]))

gray()
subplot(121)
imshow(im)
axis('off')
subplot(122)
imshow(im2)
axis('off')
show()

图中图

如下图所示,将一图融入了二图中,虽然图片有些扭曲但这就是该算法所需要呈现的效果。图像中的图像即为仿射扭曲的一个简单例子,它将图像或者图像的一部分放置在另一幅图像中,使得他们能够和指定的区域或者标记物对齐。为了能够实现更加精准的匹配,我们可以使用分段仿射扭曲
在这里插入图片描述

from numpy import *
from matplotlib.pyplot import *
from scipy import ndimage
from PIL import Image
def Haffine_from_points(fp, tp):
    """计算H仿射变换,使得tp是fp经过仿射变换H得到的"""

    if fp.shape != tp.shape:
        raise RuntimeError('number of points do not match')
        
    # 对点进行归一化(对数值计算很重要)
    # --- 映射起始点 ---
    m = mean(fp[:2], axis=1)
    maxstd = max(std(fp[:2], axis=1)) + 1e-9
    C1 = diag([1/maxstd, 1/maxstd, 1]) 
    C1[0][2] = -m[0]/maxstd
    C1[1][2] = -m[1]/maxstd
    fp_cond = dot(C1,fp)
    
    # --- 映射对应点 ---
    m = mean(tp[:2], axis=1)
    C2 = C1.copy() # 两个点集,必须都进行相同的缩放
    C2[0][2] = -m[0]/maxstd
    C2[1][2] = -m[1]/maxstd
    tp_cond = dot(C2,tp)
    
    # 因为归一化后点的均值为0,所以平移量为0
    A = concatenate((fp_cond[:2],tp_cond[:2]), axis=0)
    U,S,V = linalg.svd(A.T)
    
    # 如Hartley和Zisserman著的Multiplr View Geometry In Computer,Scond Edition所示,
    # 创建矩阵B和C
    tmp = V[:2].T
    B = tmp[:2]
    C = tmp[2:4]
    
    tmp2 = concatenate((dot(C,linalg.pinv(B)),zeros((2,1))), axis=1) 
    H = vstack((tmp2,[0,0,1]))
    
    # 反归一化
    H = dot(linalg.inv(C2),dot(H,C1))
    
    return H / H[2,2]

def image_in_image(im1, im2, tp):
    """使用仿射变换将im1放置在im2上,使im1图像的角和tp尽可能的靠近
        tp是齐次表示的,并且是按照从左上角逆时针计算的"""
    
    # 扭曲的点
    m,n = im1.shape[:2]
    fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
    
    # 计算仿射变换,并且将其应用于图像im1中
    H = Haffine_from_points(tp,fp)
    im1_t = ndimage.affine_transform(im1,H[:2,:2],
                    (H[0,2],H[1,2]),im2.shape[:2])
    alpha = (im1_t > 0)
    
    return (1-alpha)*im2 + alpha*im1_t


im1 = array(Image.open('31.jpg').convert('L'))
im2 = array(Image.open('32.jpg').convert('L'))

gray()
subplot(131)
imshow(im1)
axis('equal')
axis('off')
subplot(132)
imshow(im2)
axis('equal')
axis('off')

# 选定一些目标点
tp = array([[264, 538, 540, 264], [40, 36, 605, 605], [1, 1, 1, 1]])

im3 = image_in_image(im1, im2, tp)
subplot(133)
imshow(im3)
axis('equal')
axis('off')
show()

分段仿射扭曲

为了能够实现图像中角点的精确匹配,我们通常使用分段仿射扭曲。即首先通过三角化图像块再使用仿射扭曲来扭曲每个三角形,其中的核心部分即为图像块的三角化,可以通过狄洛克三角剖分实现,其具体如下:

在这里插入图片描述

from PIL import Image
from pylab import *
import numpy as np
from scipy.spatial import Delaunay

x,y  = array(np.random.standard_normal((2,100)))
"""centers, edges, tri, neighbors = md.delaunay(x, y"""
tri = Delaunay(np.c_[x,y]).simplices

figure()

for t in tri:
    t_ext = [t[0],t[1],t[2],t[0]]
    plot(x[t_ext], y[t_ext], 'g')

plot(x,y,'*')
axis('off')
show()

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 以下是用Python实现图像扭曲的示例代码: ```python import cv2 import numpy as np img = cv2.imread('example.png') rows, cols, ch = img.shape # 定义扭曲函数 def distort(img, fx, fy): rows, cols, ch = img.shape dest=np.zeros((rows,cols,ch),dtype=np.uint8) for i in range(rows): for j in range(cols): # 计算目标像素坐标 dest_x = i + (j - cols/2) * fx dest_y = j + (i - rows/2) * fy # 如果目标坐标在图像范围内,则进行双线性插值 if dest_x >= 0 and dest_x < rows and dest_y >= 0 and dest_y < cols: x1, x2 = int(dest_x), min(int(dest_x)+1, rows-1) y1, y2 = int(dest_y), min(int(dest_y)+1, cols-1) p1 = img[x1, y1] * (x2-dest_x)*(y2-dest_y) p2 = img[x2, y1] * (dest_x-x1)*(y2-dest_y) p3 = img[x1, y2] * (x2-dest_x)*(dest_y-y1) p4 = img[x2, y2] * (dest_x-x1)*(dest_y-y1) dest[i][j] = p1 + p2 + p3 + p4 return dest # 扭曲图像 distorted_img = distort(img, 0.002, 0.002) # 显示原始图像扭曲后的图像 cv2.imshow('Original', img) cv2.imshow('Distorted', distorted_img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 代码的核心是扭曲函数 `distort`,该函数计算目标像素坐标,然后进行双线性插值,最终返回扭曲后的图像。在主函数中,我们读入一个示例图像,并将其扭曲后展示出来。你可以尝试调整 `distort` 函数中的 `fx` 和 `fy` 参数,改变扭曲的程度。 ### 回答2: 要编写一个能够扭曲图像的代码,可以使用以下步骤: 1. 导入所需的库和模块,如OpenCV和NumPy。 2. 使用OpenCV中的`imread()`函数读取图像,并将其存储在一个变量中。 3. 计算图像的宽度和高度,以便于在后面的操作中使用。 4. 创建一个空白的图像,与原始图像具有相同的尺寸和通道数。 5. 使用`for`循环遍历原始图像中的每个像素。 6. 在遍历像素的过程中,可以根据需要对像素的位置进行扭曲操作。例如,可以计算新的位置坐标,基于原始坐标应用一些数学公式,从而改变像素的位置。 7. 根据计算得到的新的像素位置,将原始图像中的像素值复制到新的图像中的相应位置。 8. 使用OpenCV中的`imwrite()`函数将扭曲后的图像保存到本地。 下面是一个示例代码,用于将图像垂直方向上扭曲: ```python import cv2 import numpy as np # 读取图像 image = cv2.imread('input_image.jpg') # 获取图像的宽度和高度 height, width = image.shape[:2] # 创建空白图像 distorted_image = np.zeros((height, width, 3), dtype=np.uint8) # 扭曲图像 for y in range(height): for x in range(width): new_y = (y + int(20 * np.sin(2 * np.pi * x / 200))) % height # 通过正弦函数使图像扭曲 distorted_image[y, x] = image[new_y, x] # 保存扭曲后的图像 cv2.imwrite('distorted_image.jpg', distorted_image) ``` 请注意,上述代码仅提供了一个基本的图像扭曲示例。实际情况中,您可能需要根据具体需求对像素进行更复杂的扭曲操作,或者使用其他算法和技术来实现更高级的扭曲效果。 ### 回答3: 以下是一个使用Python编写的简单图像扭曲代码示例: ```python import cv2 import numpy as np def distort_image(image, strength): height, width, _ = image.shape # 创建一个空图像,用于存储扭曲后的图像 distorted_image = np.zeros(image.shape, dtype=np.uint8) for y in range(height): for x in range(width): # 根据当前像素的坐标和扭曲强度计算偏移量 offset_x = int((strength/height) * np.sin(2*np.pi*y/width)) offset_y = int((strength/width) * np.cos(2*np.pi*x/height)) # 计算扭曲后的像素位置 new_x = (x + offset_x) % width new_y = (y + offset_y) % height # 将原始图像的像素复制到扭曲图像 distorted_image[new_y, new_x] = image[y, x] return distorted_image # 读取图像 image = cv2.imread("image.jpg") # 扭曲图像 distorted_image = distort_image(image, strength=50) # 显示原始图像扭曲图像 cv2.imshow("Original Image", image) cv2.imshow("Distorted Image", distorted_image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 该代码通过使用正弦和余弦函数来计算每个像素在x和y坐标方向上的偏移量,根据这些偏移量计算出扭曲后的像素位置,并将原始图像的像素复制到相应的位置上,从而实现图像扭曲效果。在这个示例中,扭曲的强度为50,您可以根据自己的需求调整这个值来获得不同程度的图像扭曲效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值