将较小的图像覆盖在较大的图像上

你好,我正在创建一个程序,取代一个图像中的脸与其他人的脸。然而,我仍然坚持尝试将新的面孔插入到原始的、更大的图像中。我已经研究了 ROI 和 addWeight (需要图像的大小相同) ,但是我还没有找到在 python 中做到这一点的方法。任何建议都是好的。我是新来的。

我使用以下测试图像:

图片来源:

enter image description here

图片来源:

enter image description here

到目前为止,这是我的密码,混合了其他的样本:

import cv2
import cv2.cv as cv
import sys
import numpy


def detect(img, cascade):
rects = cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=3, minSize=(10, 10), flags = cv.CV_HAAR_SCALE_IMAGE)
if len(rects) == 0:
return []
rects[:,2:] += rects[:,:2]
return rects


def draw_rects(img, rects, color):
for x1, y1, x2, y2 in rects:
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)


if __name__ == '__main__':
if len(sys.argv) != 2:                                         ## Check for error in usage syntax


print "Usage : python faces.py <image_file>"


else:
img = cv2.imread(sys.argv[1],cv2.CV_LOAD_IMAGE_COLOR)  ## Read image file


if (img == None):
print "Could not open or find the image"
else:
cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")
gray = cv2.cvtColor(img, cv.CV_BGR2GRAY)
gray = cv2.equalizeHist(gray)


rects = detect(gray, cascade)


## Extract face coordinates
x1 = rects[0][3]
y1 = rects[0][0]
x2 = rects[0][4]
y2 = rects[0][5]
y=y2-y1
x=x2-x1
## Extract face ROI
faceROI = gray[x1:x2, y1:y2]


## Show face ROI
cv2.imshow('Display face ROI', faceROI)
small = cv2.imread("average_face.png",cv2.CV_LOAD_IMAGE_COLOR)
print "here"
small=cv2.resize(small, (x, y))
cv2.namedWindow('Display image')          ## create window for display
cv2.imshow('Display image', small)          ## Show image in the window


print "size of image: ", img.shape        ## print size of image
cv2.waitKey(1000)
161457 次浏览

一个简单的方法来实现你想要的:

import cv2
s_img = cv2.imread("smaller_image.png")
l_img = cv2.imread("larger_image.jpg")
x_offset=y_offset=50
l_img[y_offset:y_offset+s_img.shape[0], x_offset:x_offset+s_img.shape[1]] = s_img

the result image

更新

我猜你也想搞定阿尔法频道。这里有一个快速而肮脏的方法:

s_img = cv2.imread("smaller_image.png", -1)


y1, y2 = y_offset, y_offset + s_img.shape[0]
x1, x2 = x_offset, x_offset + s_img.shape[1]


alpha_s = s_img[:, :, 3] / 255.0
alpha_l = 1.0 - alpha_s


for c in range(0, 3):
l_img[y1:y2, x1:x2, c] = (alpha_s * s_img[:, :, c] +
alpha_l * l_img[y1:y2, x1:x2, c])

result image with alpha

基于 Fireant 上面的出色回答,这里是字母混合,但是更人性化一些。你可能需要交换 1.0-alphaalpha,这取决于你合并的方向(我的方向是从消防员的答案交换)。

o* == s_img.* b* == b_img.*

for c in range(0,3):
alpha = s_img[oy:oy+height, ox:ox+width, 3] / 255.0
color = s_img[oy:oy+height, ox:ox+width, c] * (1.0-alpha)
beta  = l_img[by:by+height, bx:bx+width, c] * (alpha)


l_img[by:by+height, bx:bx+width, c] = color + beta

使用@fireant 的想法,我编写了一个函数来处理覆盖。这对任何位置参数(包括负位置)都适用。

def overlay_image_alpha(img, img_overlay, x, y, alpha_mask):
"""Overlay `img_overlay` onto `img` at (x, y) and blend using `alpha_mask`.


`alpha_mask` must have same HxW as `img_overlay` and values in range [0, 1].
"""
# Image ranges
y1, y2 = max(0, y), min(img.shape[0], y + img_overlay.shape[0])
x1, x2 = max(0, x), min(img.shape[1], x + img_overlay.shape[1])


# Overlay ranges
y1o, y2o = max(0, -y), min(img_overlay.shape[0], img.shape[0] - y)
x1o, x2o = max(0, -x), min(img_overlay.shape[1], img.shape[1] - x)


# Exit if nothing to do
if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o:
return


# Blend overlay within the determined ranges
img_crop = img[y1:y2, x1:x2]
img_overlay_crop = img_overlay[y1o:y2o, x1o:x2o]
alpha = alpha_mask[y1o:y2o, x1o:x2o, np.newaxis]
alpha_inv = 1.0 - alpha


img_crop[:] = alpha * img_overlay_crop + alpha_inv * img_crop

示例用法:

import numpy as np
from PIL import Image


# Prepare inputs
x, y = 50, 0
img = np.array(Image.open("img_large.jpg"))
img_overlay_rgba = np.array(Image.open("img_small.png"))


# Perform blending
alpha_mask = img_overlay_rgba[:, :, 3] / 255.0
img_result = img[:, :, :3].copy()
img_overlay = img_overlay_rgba[:, :, :3]
overlay_image_alpha(img_result, img_overlay, x, y, alpha_mask)


# Save result
Image.fromarray(img_result).save("img_result.jpg")

结果:

img_result.jpg

如果遇到错误或异常输出,请确保:

  • img 不应该包含一个 alpha 通道(例如,如果它是 RGBA,首先转换为 RGB)
  • img_overlay拥有与 img相同数量的信道。

当您尝试使用上述任何一个答案写入目标图像时,您会得到以下错误:

ValueError: assignment destination is read-only

一个可能的快速修复方法是将 WRITEABLE 标志设置为 true。

img.setflags(write=1)

对于只是向 s _ img 添加 alpha 通道,我只在行前使用 cv2.addWeight l_img[y_offset:y_offset+s_img.shape[0], x_offset:x_offset+s_img.shape[1]] = s_img

如下:
s_img=cv2.addWeighted(l_img[y_offset:y_offset+s_img.shape[0], x_offset:x_offset+s_img.shape[1]],0.5,s_img,0.5,0)

这就是:

def put4ChannelImageOn4ChannelImage(back, fore, x, y):
rows, cols, channels = fore.shape
trans_indices = fore[...,3] != 0 # Where not transparent
overlay_copy = back[y:y+rows, x:x+cols]
overlay_copy[trans_indices] = fore[trans_indices]
back[y:y+rows, x:x+cols] = overlay_copy


#test
background = np.zeros((1000, 1000, 4), np.uint8)
background[:] = (127, 127, 127, 1)
overlay = cv2.imread('imagee.png', cv2.IMREAD_UNCHANGED)
put4ChannelImageOn4ChannelImage(background, overlay, 5, 5)

一个简单的4on4粘贴函数

def paste(background,foreground,pos=(0,0)):
#get position and crop pasting area if needed
x = pos[0]
y = pos[1]
bgWidth = background.shape[0]
bgHeight = background.shape[1]
frWidth = foreground.shape[0]
frHeight = foreground.shape[1]
width = bgWidth-x
height = bgHeight-y
if frWidth<width:
width = frWidth
if frHeight<height:
height = frHeight
# normalize alpha channels from 0-255 to 0-1
alpha_background = background[x:x+width,y:y+height,3] / 255.0
alpha_foreground = foreground[:width,:height,3] / 255.0
# set adjusted colors
for color in range(0, 3):
fr = alpha_foreground * foreground[:width,:height,color]
bg = alpha_background * background[x:x+width,y:y+height,color] * (1 - alpha_foreground)
background[x:x+width,y:y+height,color] = fr+bg
# set adjusted alpha and denormalize back to 0-255
background[x:x+width,y:y+height,3] = (1 - (1 - alpha_foreground) * (1 - alpha_background)) * 255
return background

一个简单的函数,它将图像 front闪烁到图像 back上并返回结果。它同时处理3和4通道图像,并处理 alpha 通道。重叠部分也会得到处理。

输出图像的大小与背面相同,但总是4个通道。
输出 α 通道由(u + v)/(1 + uv)给出,其中 u,v 是前后图像的 α 通道,-1 < = u,v < = 1。如果与前面没有重叠,则从后面获取 alpha 值。

import cv2


def merge_image(back, front, x,y):
# convert to rgba
if back.shape[2] == 3:
back = cv2.cvtColor(back, cv2.COLOR_BGR2BGRA)
if front.shape[2] == 3:
front = cv2.cvtColor(front, cv2.COLOR_BGR2BGRA)


# crop the overlay from both images
bh,bw = back.shape[:2]
fh,fw = front.shape[:2]
x1, x2 = max(x, 0), min(x+fw, bw)
y1, y2 = max(y, 0), min(y+fh, bh)
front_cropped = front[y1-y:y2-y, x1-x:x2-x]
back_cropped = back[y1:y2, x1:x2]


alpha_front = front_cropped[:,:,3:4] / 255
alpha_back = back_cropped[:,:,3:4] / 255
    

# replace an area in result with overlay
result = back.copy()
print(f'af: {alpha_front.shape}\nab: {alpha_back.shape}\nfront_cropped: {front_cropped.shape}\nback_cropped: {back_cropped.shape}')
result[y1:y2, x1:x2, :3] = alpha_front * front_cropped[:,:,:3] + (1-alpha_front) * back_cropped[:,:,:3]
result[y1:y2, x1:x2, 3:4] = (alpha_front + alpha_back) / (1 + alpha_front*alpha_back) * 255


return result

我重新设计了@fireant 的概念,允许使用可选的 alpha 掩码,并允许使用任何 x 或 y,包括图像边界之外的值。它会突破界限。

def overlay_image_alpha(img, img_overlay, x, y, alpha_mask=None):
"""Overlay `img_overlay` onto `img` at (x, y) and blend using optional `alpha_mask`.


`alpha_mask` must have same HxW as `img_overlay` and values in range [0, 1].
"""


if y < 0 or y + img_overlay.shape[0] > img.shape[0] or x < 0 or x + img_overlay.shape[1] > img.shape[1]:
y_origin = 0 if y > 0 else -y
y_end = img_overlay.shape[0] if y < 0 else min(img.shape[0] - y, img_overlay.shape[0])


x_origin = 0 if x > 0 else -x
x_end = img_overlay.shape[1] if x < 0 else min(img.shape[1] - x, img_overlay.shape[1])


img_overlay_crop = img_overlay[y_origin:y_end, x_origin:x_end]
alpha = alpha_mask[y_origin:y_end, x_origin:x_end] if alpha_mask is not None else None
else:
img_overlay_crop = img_overlay
alpha = alpha_mask


y1 = max(y, 0)
y2 = min(img.shape[0], y1 + img_overlay_crop.shape[0])


x1 = max(x, 0)
x2 = min(img.shape[1], x1 + img_overlay_crop.shape[1])


img_crop = img[y1:y2, x1:x2]
img_crop[:] = alpha * img_overlay_crop + (1.0 - alpha) * img_crop if alpha is not None else img_overlay_crop