用关键点对齐并拉伸两张图片的OpenCV操作

3

我有两张图片

image1 (对象):

输入图像描述]3

没有标记关键点的原始图片:

enter image description here

image2是一张白色图片(500x500)

我在image1和image2中标记了关键点。 我想通过关键点将image1对齐到image2上。因此,目标是通过拉伸、缩放和转换image2来使两个关键点重叠。

这是我的关键点(csv文件)。坐标是image1中的x和y以及image2中的x和y。

object1_x,object1_y,image_x,image_y
0,0,80,137
286,0,409,42
286,198,416,390
174,198,331,384
158,116,291,119
0,97,111,311

我该如何使用opencv和python完成这个任务?因此,结果图像应该是这样的(没有红点,红点只用于演示关键点):

enter image description here


三角网格扭曲 - fmw42
1个回答

6

概念

从第一个关键点集中提取一组包含三个索引的集合,这些索引会在两个关键点集中被索引时形成三角形。使用这些索引,我们可以从两个关键点集中获取相应的三角形,从而允许我们通过每个三角形逐个构建扭曲图像 (有关详细信息,请参见使用OpenCV将一个三角形扭曲到另一个三角形

image1.png (添加了点)

enter image description here

image2.png (添加了点)

enter image description here

结果 (添加了点)

enter image description here

代码

import cv2
import numpy as np

def triangles(points):
    points = np.where(points, points, 1)
    subdiv = cv2.Subdiv2D((*points.min(0), *points.max(0)))
    subdiv.insert(list(points))
    for pts in subdiv.getTriangleList().reshape(-1, 3, 2):
        yield [np.where(np.all(points == pt, 1))[0][0] for pt in pts]

def crop(img, pts):
    x, y, w, h = cv2.boundingRect(pts)
    img_cropped = img[y: y + h, x: x + w]
    pts[:, 0] -= x
    pts[:, 1] -= y
    return img_cropped, pts

def warp(img1, img2, pts1, pts2): 
    for indices in triangles(pts1):
        img1_cropped, triangle1 = crop(img1, pts1[indices])
        img2_cropped, triangle2 = crop(img2, pts2[indices])
        transform = cv2.getAffineTransform(np.float32(triangle1), np.float32(triangle2))
        img2_warped = cv2.warpAffine(img1_cropped, transform, img2_cropped.shape[:2][::-1], None, cv2.INTER_LINEAR, cv2.BORDER_REFLECT_101)
        mask = np.zeros_like(img2_cropped)
        cv2.fillConvexPoly(mask, np.int32(triangle2), (1, 1, 1), 16, 0)
        img2_cropped *= 1 - mask
        img2_cropped += img2_warped * mask

img1 = cv2.imread("image1.png")
img2 = cv2.imread("image2.png")

pts1 = np.array([[0, 0], [286, 0], [286, 198], [174, 198], [158, 116], [0, 97]])
pts2 = np.array([[80, 37], [409, 42], [416, 390], [331, 384], [291, 119], [111, 311]])

warp(img1, img2, pts1, pts2)

for pt in pts2:
    cv2.circle(img2, tuple(pt), 15, (0, 0, 255), -1)

cv2.imshow("Original", img1)
cv2.imshow("Transformed", img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出

在此输入图像描述

解释

  1. 导入必要的库:
import cv2
import numpy as np

定义一个函数triangles,它将接收一个坐标数组points,并生成由原始坐标数组的三个索引构成的三角形列表以覆盖该坐标数组的区域。
def triangles(points):
    points = np.where(points, points, 1)
    subdiv = cv2.Subdiv2D((*points.min(0), *points.max(0)))
    subdiv.insert(list(points))
    for pts in subdiv.getTriangleList().reshape(-1, 3, 2):
        yield [np.where(np.all(points == pt, 1))[0][0] for pt in pts]

定义一个函数,名为crop,它将接受一个图像数组img和一个由三个坐标pts组成的数组。它会返回一个矩形片段,其大小恰好足以适应由这三个点形成的三角形,并将三个坐标转换为图像左上角的位置,并返回此新坐标数组。
def crop(img, pts):
    x, y, w, h = cv2.boundingRect(pts)
    img_cropped = img[y: y + h, x: x + w]
    pts[:, 0] -= x
    pts[:, 1] -= y
    return img_cropped, pts

定义一个名为 warp 的函数,该函数将接受两个图片数组 img1img2,以及两个坐标数组 pts1pts2。它将利用之前定义的 triangles 函数来迭代第一个坐标数组中的三角形,使用之前定义的 crop 函数在对应于三角形索引的坐标处裁剪两张图片,并使用 cv2.warpAffine() 方法来扭曲当前迭代中的三角形图像。
def warp(img1, img2, pts1, pts2): 
    for indices in triangles(pts1):
        img1_cropped, triangle1 = crop(img1, pts1[indices])
        img2_cropped, triangle2 = crop(img2, pts2[indices])
        transform = cv2.getAffineTransform(np.float32(triangle1), np.float32(triangle2))
        img2_warped = cv2.warpAffine(img1_cropped, transform, img2_cropped.shape[:2][::-1], None, cv2.INTER_LINEAR, cv2.BORDER_REFLECT_101)
        mask = np.zeros_like(img2_cropped)
        cv2.fillConvexPoly(mask, np.int32(triangle2), (1, 1, 1), 16, 0)
        img2_cropped *= 1 - mask
        img2_cropped += img2_warped * mask
  1. 读取图像。在这种情况下,img1是我们想要变形的图像,img2是空白的500 x 500图像。同时,定义两个坐标数组作为图像的关键点:
img1 = cv2.imread("image1.png")
img2 = cv2.imread("image2.png")

pts1 = np.array([[0, 0], [286, 0], [286, 198], [174, 198], [158, 116], [0, 97]])
pts2 = np.array([[80, 37], [409, 42], [416, 390], [331, 384], [291, 119], [111, 311]])

最后,使用先前定义的warp函数将img1扭曲,使其关键点与img2的关键点重叠,并显示结果图像。我将第二个坐标数组中的点绘制到结果扭曲图像上,以便更容易地可视化扭曲过程:
warp(img1, img2, pts1, pts2)

for pt in pts2:
    cv2.circle(img2, tuple(pt), 15, (0, 0, 255), -1)

cv2.imshow("Original", img1)
cv2.imshow("Transformed", img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接