如何使用Python用新图像替换图像中的轮廓(矩形)?

时间:2016-07-12 05:46:18

标签: python opencv image-processing computer-vision pillow

我目前正在使用opencv(CV2)和Python Pillow图像库来尝试拍摄任意手机的图像并用新图像替换屏幕。我已经到了可以拍摄图像并识别手机屏幕并获得角落所有坐标的地步,但是我很难用新图像替换图像中的那个区域。 / p>

到目前为止我的代码:

import cv2
from PIL import Image

image = cv2.imread('mockup.png')
edged_image = cv2.Canny(image, 30, 200)

(contours, _) = cv2.findContours(edged_image.copy(), cv2.RETR_TREE,     cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key = cv2.contourArea, reverse = True)[:10]
screenCnt = None

for contour in contours:
    peri = cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(contour, 0.02 * peri, True)

    # if our approximated contour has four points, then
    # we can assume that we have found our screen
    if len(approx) == 4:
        screenCnt = approx
        break

cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 3)
cv2.imshow("Screen Location", image)
cv2.waitKey(0)

这会给我一个如下图像:enter image description here

我还可以使用以下代码行获取屏幕角落的坐标:

screenCoords = [x[0].tolist() for x in screenCnt] 
// [[398, 139], [245, 258], [474, 487], [628, 358]]

然而,我无法弄清楚我的生活如何拍摄新图像并将其缩放为我找到的坐标空间的形状并将图像覆盖在顶部。

我的猜测是我可以使用Pillow中的图像变换使用我从this stackoverflow question改编的函数来完成此操作:

def find_transform_coefficients(pa, pb):
"""Return the coefficients required for a transform from start_points to end_points.

    args:
        start_points -> Tuple of 4 values for start coordinates
        end_points --> Tuple of 4 values for end coordinates
"""
matrix = []
for p1, p2 in zip(pa, pb):
    matrix.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]])
    matrix.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]])

A = numpy.matrix(matrix, dtype=numpy.float)
B = numpy.array(pb).reshape(8)

res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B)
return numpy.array(res).reshape(8) 

然而,我有点过头了,我无法正确掌握细节。有人可以给我一些帮助吗?

修改

好了,我现在正在使用getPerspectiveTransform和warpPerspective函数,我还有以下附加代码:

screenCoords = numpy.asarray(
    [numpy.asarray(x[0], dtype=numpy.float32) for x in screenCnt],
    dtype=numpy.float32
)

overlay_image = cv2.imread('123.png')
overlay_height, overlay_width = image.shape[:2]

input_coordinates = numpy.asarray(
    [
        numpy.asarray([0, 0], dtype=numpy.float32),
        numpy.asarray([overlay_width, 0], dtype=numpy.float32),
        numpy.asarray([overlay_width, overlay_height],     dtype=numpy.float32),
        numpy.asarray([0, overlay_height], dtype=numpy.float32)
    ],
    dtype=numpy.float32,
)

transformation_matrix = cv2.getPerspectiveTransform(
    numpy.asarray(input_coordinates),
    numpy.asarray(screenCoords),
)

warped_image = cv2.warpPerspective(
    overlay_image,
    transformation_matrix,
    (background_width, background_height),
)
cv2.imshow("Overlay image", warped_image)
cv2.waitKey(0)

图像正在旋转并正确倾斜(我认为),但它与屏幕尺寸不同。它的“更短”:

enter image description here

如果我使用垂直高度不同的图像,我最终得到的东西太长了“

enter image description here

我是否需要应用其他转换来缩放图像?不知道这里发生了什么,我认为透视变换会使图像自动缩小到提供的坐标。

2 个答案:

答案 0 :(得分:2)

我下载了您的图像数据并在我的本地计算机中重现了该问题以找出解决方案。还下载了lenna.png以适应手机屏幕。

import cv2
import numpy as np

# Template image of iPhone
img1 = cv2.imread("/Users/anmoluppal/Downloads/46F1U.jpg")
# Sample image to be used for fitting into white cavity
img2 = cv2.imread("/Users/anmoluppal/Downloads/Lenna.png")

rows,cols,ch = img1.shape

# Hard coded the 3 corner points of white cavity labelled with green rect.
pts1 = np.float32([[201, 561], [455, 279], [742, 985]])
# Hard coded the same points on the reference image to be fitted.
pts2 = np.float32([[0, 0], [512, 0], [0, 512]])

# Getting affine transformation form sample image to template.
M = cv2.getAffineTransform(pts2,pts1)

# Applying the transformation, mind the (cols,rows) passed, these define the final dimensions of output after Transformation.
dst = cv2.warpAffine(img2,M,(cols,rows))

# Just for Debugging the output.
final = cv2.addWeighted(dst, 0.5, img1, 0.5, 1)
cv2.imwrite("./garbage.png", final)

enter image description here

答案 1 :(得分:0)

您可以通过

将新图像(旋转到手机的屏幕方向)叠加到原始图像上
import cv2
A_img = cv2.imread("new_image.png")
B_img = cv2.imread("larger_image.jpg")
x_offset=y_offset=50
B_img[y_offset:y_offset+A_img.shape[0], x_offset:x_offset+A_img.shape[1]] = A_img

如果需要,您可以使用Alpha通道正确旋转新图像。

正如您在下面的评论中所提到的(在标题透视转换下),新图像需要进行透视变换(扭曲)。请查看下面的链接,了解透视转换如何将扭曲的图像修正为直线(您希望与此相反)。

http://docs.opencv.org/master/da/d6e/tutorial_py_geometric_transformations.html#gsc.tab=0

你基本上需要在原始和扭曲空间(pts1和pts2)中提供4个点进行转换。

我认为您可以使用原始图像的四个角来插入(pts1),轮廓的角(pts2)应该可以使用。