如何在Python中使用OpenCV处理图像并将一个小图像居中粘贴到另一个大图像上?

3
我正在学习OpenCV,试图将一张小图片贴在一张大图片上面。但是,它显示了一个错误,因为两张图片应该拥有相同的大小。我也尝试遵循提供的建议(如如何使用Pillow将图片粘贴到更大的图片上?如何使用PIL在Python中将一个图像合成到另一个图像上?
   import cv2 as cv
   from scipy import ndimage

   img1 = cv.imread('Please put your file name')

   top_left_x = min([x1,x2,x3,x4])
   top_left_y = min([y1,y2,y3,y4])
   bot_right_x = max([x1,x2,x3,x4])
   bot_right_y = max([y1,y2,y3,y4])

   y_right =bot_right_y + 1
   x_right =bot_right_x + 1

  cropped = img[top_left_y: y_right, top_left_x: x_right]

  rotate = ndimage.rotate(cropped, ang)

最终输出的图像应该位于中心位置。

可能是如何在Python中使用PIL将图像合成到另一个图像上?的重复问题。 - Rajkamal Mishra
2个回答

6
这是一个纯粹的 PIL 解决方案:-
from PIL import Image

img1 = Image.open(r"Source_Image_path")

# The values used to crop the original image (will form a bbox)
x1, y1, x2, y2 = 10, 10, 400, 400

# The angle at which the cropped Image must be rotated
angle = 50

# cropping the original image 
img = img1.crop((x1, y1, x2, y2))

# Firstly converting the Image mode to RGBA, and then rotating it
img = img.convert("RGBA").rotate(angle, resample=Image.BICUBIC)

# calibrating the bbox for the beginning and end position of the cropped image in the original image 
# i.e the cropped image should lie in the center of the original image
x1 = int(.5 * img1.size[0]) - int(.5 * img.size[0])
y1 = int(.5 * img1.size[1]) - int(.5 * img.size[1])
x2 = int(.5 * img1.size[0]) + int(.5 * img.size[0])
y2 = int(.5 * img1.size[1]) + int(.5 * img.size[1])

# pasting the cropped image over the original image, guided by the transparency mask of cropped image
img1.paste(img, box=(x1, y1, x2, y2), mask=img)

# converting the final image into its original color mode, and then saving it
img1.convert(img1.mode).save("Destination_path")

输入图像:

enter image description here

输出图像:

enter image description here

代码本身很好理解,但你可能想知道为什么我们要将裁剪后的图像反复转换为RGBA。原因是,如果我们在PIL中旋转非 alpha 图像,则图像边缘会出现黑色条/边,这些像素值已不再存在(请阅读此问题了解更多信息)。但是,如果我们对 alpha 图像进行相同操作,即通过 rotate() 传递一个 alpha 图像,则空像素值最终变成完全透明(或 alpha = 0)。


不错,不过我会使用 rotate(angle, resample=Image.BICUBIC, expand=True)。这样可以获得更好的输出质量(默认情况下,旋转使用最近邻插值,边缘效果很差),而且不会裁剪旋转后的补丁的角落。不幸的是,仍然会从 paste 得到一个丑陋的边缘,我不确定如何解决它。 - jcupitt
@jcupitt 重新采样绝对是一个不错的选择。只有在 OP 想要将旋转后的图像拉伸到其完整长度时,扩展才是可行的,我的意思是旋转后的图像应该适合输出框架。我不太确定您所说的丑陋边缘是什么意思?黑色条纹在图像数据不存在的地方吗? - Vasu Deo.S
尝试在您的代码中添加“expand”,然后再次运行它,您会发现您得到了完整的像素正方形,而不是被削减的八边形。它不会拉伸它,只是不会裁剪它。将其保存为PNG文件,然后尝试放大粘贴的斜边。您会发现没有抗锯齿效果,而是一个锯齿状的楼梯形。 - jcupitt
@jcupitt,我觉得你错过了我的第一条评论,我清楚地说明了expand会做什么,以及重新采样为什么是一个好的选择(同意你所说的两个观点)。我唯一的疑问是因为你说“你仍然会从粘贴中得到一个丑陋的边缘,不幸的是,我不知道如何解决这个问题”,我无法理解你所提到的“丑陋的边缘”是什么? - Vasu Deo.S
丑陋的边缘是在你“粘贴”时缺乏抗锯齿 -- 你会在粘贴的图像边缘看到阶梯状。很遗憾,我不知道如何在PIL中修复这个问题。 - jcupitt

0

虽然目前可用的解决方案都没有对OpenCV产生作用,也没有解决我的问题。但是,在多次修改后,我已经成功解决了OpenCV中的这个问题。

import cv2
img = cv2.imread('Please put your file name')

# Setting the parameters
ang = 47
top_left_x = min([12,42,53,11])
top_left_y = min([41,56,75,20])
bot_right_x = max([12,42,53,11])
bot_right_y = max([41,56,75,20])

y_right =bot_right_y + 1
x_right =bot_right_x + 1

# Cropping the image
cropped_img = img[top_left_y: y_right, top_left_x: x_right]

###########  Rotating the image ##########
# First setting the centre and roatation angle parameter. 
# To rotate sub-image from the centre of the original image
rows,cols = img.shape
M = cv2.getRotationMatrix2D((cols/2,rows/2),ang,1)

# Setting the size with original image to resolve size issue
rotated_img = cv2.warpAffine(cropped_img,M,(img.shape[1],img.shape[0]))

# Pasting the rotated image on original image
# The original image will be in the background with transparency 0.3
# The sub-image will be pasted above the original image with transparency 0.7
img = cv2.addWeighted(img, 0.3, rotated_img, 0.7, 0)

# Showing the image
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.imshow('output_image.jpg', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

这可能不是最合适的答案,但对我们许多人来说可以达到目的。


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