【问题标题】:overlay a smaller image on a larger image python OpenCv在较大的图像上覆盖较小的图像 python OpenCv
【发布时间】:2012-12-13 08:07:45
【问题描述】:

您好,我正在创建一个程序,用别人的脸替换图像中的脸。但是,我坚持尝试将新面孔插入到原始的更大图像中。我研究了 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)              

【问题讨论】:

    标签: python opencv object-detection


    【解决方案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 通道示例中添加一个解释吗?我正在进入 cv2 和 python,这些东西对我来说仍然是一个巨大的问号
    • 同意乔纳森的请求。我想知道数学在做什么,以便更好地调试问题
    • @JonathanCrowe 将 image1 覆盖在 imag2 上,[result-image::rgb channel] = [image1::rgb-channel] * [imag1::alpha-channel] + [image2::rgb-通道] * (1.0-[imag1::alpha-channel])。
    • @Adib 看到上面的评论
    • 嘿,在更新中你的行被切断了:l_img[y_offset:y_offset+s_img.shape[0], x_offset:x_offset+s_img.shape[1], c] = 你是什么意思?
    【解决方案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")
    

    结果:

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

    • img 不应包含 Alpha 通道。 (例如,如果是 RGBA,则先转换为 RGB。)
    • img_overlay 的频道数与img 相同。

    【讨论】:

    • IndexError: index 3 is out of bounds for axis 2 with size 3 是这个错误。
    • 如何将较大图像的质心与较小图像的质心重叠?我已经有了这两个图像的质心。我用了上面的函数,但是小图最左边的像素会自动叠加到大图上。
    • @Schütze 源图像必须转换为 RGBA,例如。 img = cv2.cvtColor(img, cv2.COLOR_RGB2RGBA).copy()
    • 如果 img 不应该有 alpha,也许函数会从检查中受益?他们必须拥有相同数量的频道?假设有一种标准化的检查方式。
    【解决方案3】:

    根据上面 fireant 的出色回答,这里是 alpha 混合,但更易读于人类。您可能需要交换 1.0-alphaalpha,具体取决于您要合并的方向(我的是从 fireant 的答案中交换的)。

    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

        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
        

        【讨论】:

        • 这是我在这里唯一可以执行的东西,但由于某种原因,它混合得非常糟糕,前面的图像颜色设法与背景或其他东西混合在一起。
        • 等一下,我后来在自己的代码中改了一些东西,我去看看
        • 这是一种混合点,虽然当 alpha_front
        • 这是我遇到的问题:click here 最后我合并了 12 种不同的方法,并使用 addWeighted() 解决了这个问题,并在顶部的编辑器中将背景图像修改为黑色图像将被放置。在我的情况下,前/顶部图像没有任何透明度(或者更确切地说,我不在乎它是否有)所以这对我有用。 see here for that result
        • 我编辑了这个答案以包含我的更改。重要的是倒数第二行的alpha_back * back_cropped[:,:,:3] 更改为(1-alpha_front) * back_cropped[:,:,:3]。因为结果图像的 alpha 通道中已经考虑了 alpha 反向通道。
        【解决方案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
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-03-07
                • 2018-02-08
                相关资源
                最近更新 更多