#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()
Вывод
Объяснение
- Импортируйте необходимые библиотеки:
import cv2
import numpy as np
- Определите функцию,
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]
- Определите функцию,
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
, которая будет принимать 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
- Читайте в своих изображениях. В вашем случае
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]])
- Наконец, используйте
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()