opencv выравнивает два изображения по ключевым точкам с растяжением

#python #opencv #image-processing

Вопрос:

У меня есть два изображения

изображение 1 (объект):

введите описание изображения здесь]3

Исходное изображение без отмеченных ключевых точек:

введите описание изображения здесь

изображение 2-это белое изображение (500х500)

В image1 и в image2 я отметил ключевые точки. Я хочу выровнять изображение 1 на изображении 2 по ключевым точкам. Таким образом, цель, чтобы обе ключевые точки совпадали с растяжением, масштабированием и преобразованием изображения2.

Это мои ключевые точки (csv-файл). Координаты x и y для изображения 1 в изображении 1 и для изображения 2 в изображении 2.

 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?
Таким образом, изображение результата должно выглядеть следующим образом (без красных точек, красные точки предназначены только для демонстрации ключевых точек).:

введите описание изображения здесь

Комментарии:

1. основа треугольной сетки

Ответ №1:

Концепция

Извлеките наборы из 3 индексов из первого набора ключевых точек, которые будут образовывать треугольники при индексировании из обоих наборов ключевых точек. С помощью индексов мы можем получить соответствующие треугольники из обоих наборов ключевых точек, что позволяет нам строить треугольник искаженного изображения за треугольником (более подробную информацию см. В разделе Преобразование одного треугольника в другой с помощью OpenCV).:

image1.png (с добавлением очков):

введите описание изображения здесь

image2.png (с добавлением очков):

введите описание изображения здесь

Результат (с добавлением очков)

введите описание изображения здесь

Код

 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
 
  1. Определите функцию, triangles , которая будет принимать массив координат points , и выдаст списки из 3 индексов массива для треугольников , которые будут покрывать область исходного массива координат:
 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]
 
  1. Определите функцию, 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
 
  1. Определите функцию, warp , которая будет принимать 2 массива изображений, img1 и img2 , и 2 массива координат, pts1 и pts2 . Он будет использовать 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. Кроме того, определите 2 массива координат, которые будут ключевыми точками изображений:
 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]])
 
  1. Наконец, используйте 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()