在较大的图像上叠加较小的图像python OpenCv

2025-03-04 08:24:00
admin
原创
58
摘要:问题描述:嗨,我正在创建一个程序,用别人的脸替换图像中的一张脸。但是,我无法将新脸插入原始的、更大的图像中。我研究过 ROI 和 addWeight(需要图像大小相同),但我还没有找到在 python 中执行此操作的方法。任何建议都很好。我是 opencv 的新手。我正在使用以下测试图像:较小的图像:更大的图...

问题描述:

嗨,我正在创建一个程序,用别人的脸替换图像中的一张脸。但是,我无法将新脸插入原始的、更大的图像中。我研究过 ROI 和 addWeight(需要图像大小相同),但我还没有找到在 python 中执行此操作的方法。任何建议都很好。我是 opencv 的新手。

我正在使用以下测试图像:

较小的图像:

在此处输入图片描述

更大的图像:

在此处输入图片描述

这是我迄今为止的代码……其他样本的混合器:

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)              

解决方案 1:

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

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

结果图像

更新

我猜你也想处理 alpha 通道。这里有一个快速而粗略的方法:

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])

带 alpha 的结果图像

解决方案 2:

使用@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")

结果:

结果.jpg

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

  • img 不应包含 alpha 通道。(例如,如果是 RGBA,则先转换为 RGB。)

  • img_overlay具有与 相同数量的通道img

解决方案 3:

根据上面 fireant 的出色回答,以下是 alpha 混合,但更易于理解。您可能需要根据合并的方向交换1.0-alpha和(我的是从 fireant 的回答交换的)。alpha

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

解决方案 4:

这里是:

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)

解决方案 5:

一个简单的函数,将一个图像块化front到另一幅图像上back并返回结果。它适用于 3 通道和 4 通道图像并处理 alpha 通道。重叠也会被处理。

输出图像与背景图像大小相同,但始终为 4 个通道。

输出 alpha 通道由 (u+v)/(1+uv) 给出,其中 u、v 是前后图像的 alpha 通道,且 -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}
ab: {alpha_back.shape}
front_cropped: {front_cropped.shape}
back_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

解决方案 6:

为了给 s_img 添加一个 alpha 通道,我只需在行前使用 cv2.addWeighted
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)

解决方案 7:

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

ValueError: assignment destination is read-only

一个快速的潜在解决方法是将 WRITEABLE 标志设置为 true。

img.setflags(write=1)

解决方案 8:

一个简单的 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

解决方案 9:

我重新设计了 @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
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3878  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2714  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   47  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   48  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   48  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用