【问题标题】:How to make a single image using several images?如何使用多个图像制作单个图像?
【发布时间】:2022-01-26 04:04:45
【问题描述】:

我有这些图像,所有图像中都有阴影。我的目标是使用这三个图像制作没有阴影的汽车的单个图像:

最后,我怎样才能得到如下所示的这种图像:

感谢任何帮助或建议。

已编辑

根据 cmets,我使用了np.maximum 并轻松实现了我的目标:

import cv2
import numpy as np

img_1 = cv2.imread('1.png', cv2.COLOR_BGR2RGB)
img_2 = cv2.imread('2.png', cv2.COLOR_BGR2RGB)

img = np.maximum(img_1, img_2)

cv2.imshow('img1', img_1)
cv2.imshow('img2', img_2)

cv2.imshow('img', img)
cv2.waitKey(0)

【问题讨论】:

  • 你已经知道每张图片的阴影在哪里了吗?
  • 不,我不知道。
  • 针对上图的解决方案:如果您检查第一行中的所有像素(x 坐标 = 0),它们是否与白色完全匹配,您可以确定灰色框的位置,一旦您知道盒子在哪里,那么这个过程就很简单了。
  • 让我看看我是否正确理解了您的问题。您有三张带有“阴影”(灰色矩形)的汽车图像。您需要从所有三个图像中切出阴影并将它们连接起来以创建一个没有阴影的合成图像。对吗?
  • 只取每个图像之间每个像素的最大值。每个像素的 Max(image1, image2, image3)。您可以使用 Numpy 执行此操作,例如使用 np.max(image1,np.max(image2,image3)) 请参阅numpy.org/doc/stable/reference/generated/numpy.maximum.html。图像必须事先对齐以匹配比例和方向以及位置和形状。

标签: python opencv opencv3.0


【解决方案1】:

这是一个可能的解决方案。总体思路是计算阴影的位置,生成一个标识阴影位置的二进制掩码,并使用此信息从所有裁剪的子图像中复制像素。

让我们看看代码。第一个问题是定位三个图像。我使用黑盒分割和裁剪每辆车,如下所示:

# Imports:
import cv2
import numpy as np

# image path
path = "D://opencvImages//"
fileName = "qRLI7.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Get the HSV image:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)

# Get the grayscale image:
grayImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
showImage("grayImage", grayImage)

# Threshold via Otsu:
_, binaryImage = cv2.threshold(grayImage, 5, 255, cv2.THRESH_BINARY_INV)

cv2.imshow("binaryImage", binaryImage)
cv2.waitKey(0)

前一位使用图像的grayscale 版本,并使用5 的阈值应用固定二值化。我还预先计算了原始图像的HSV 版本。阈值化的结果是这样的:

我正在尝试获取黑色矩形并使用它们来裁剪每辆车。让我们获取轮廓并按区域过滤它们,因为二值图像上的黑色矩形面积最大:

for i, c in enumerate(currentContour):

    # Get the contour's bounding rectangle:
    boundRect = cv2.boundingRect(c)

    # Get the dimensions of the bounding rect:
    rectX = boundRect[0]
    rectY = boundRect[1]
    rectWidth = boundRect[2]
    rectHeight = boundRect[3]

    # Get the area:
    blobArea = rectWidth * rectHeight
    minArea = 20000

    if blobArea > minArea:

        # Deep local copies:
        hsvImage = hsvImage.copy()
        localImage = inputImage.copy()

        # Get the S channel from the HSV image:
        (H, S, V) = cv2.split(hsvImage)

        # Crop image:
        croppedImage = V[rectY:rectY + rectHeight, rectX:rectX + rectWidth]
        localImage = localImage[rectY:rectY + rectHeight, rectX:rectX + rectWidth]

        _, binaryMask = cv2.threshold(croppedImage, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)

过滤每个轮廓得到最大的后,我需要定位阴影的位置。阴影在HSV 色彩空间中大部分可见,特别是在V 通道中。我裁剪了图像的两个版本:原始的BGR 图像,现在被裁剪,以及HSV 图像的V 裁剪通道。这是在S 通道上应用自动阈值处理产生的二进制掩码:

要定位阴影,我只需要起始 x 坐标和它的 width,因为每个裁剪图像的阴影都是均匀的。它的height 等于每个裁剪图像的高度。我使用SUM 模式将V 图像缩小为一行。这将对所有列中的每个像素求和。最大值将对应阴影的位置:

        # Image reduction:
        reducedImg = cv2.reduce(binaryMask, 0, cv2.REDUCE_SUM, dtype=cv2.CV_32S)
    
        # Normalize image:
        max = np.max(reducedImg)
        reducedImg = reducedImg / max

        # Clip the values to [0,255]
        reducedImg = np.clip((255 * reducedImg), 0, 255)

        # Convert the mat type from float to uint8:
        reducedImg = reducedImg.astype("uint8")

        _, shadowMask = cv2.threshold(reducedImg, 250, 255, cv2.THRESH_BINARY)

缩小的图像只是一行:

白色像素表示最大值。阴影的位置绘制成一条面积最大的水平线,也就是最连续的白色像素。我通过获取轮廓并再次过滤到最大区域来处理这一行:

        # Get the biggest rectangle:
        subContour, _ = cv2.findContours(shadowMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        for j, s in enumerate(subContour):

            # Get the contour's bounding rectangle:
            boundRect = cv2.boundingRect(s)

            # Get the dimensions of the bounding rect:
            rectX = boundRect[0]
            rectY = boundRect[1]
            rectWidth = boundRect[2]
            rectHeight = boundRect[3]

            # Get the area:
            blobArea = rectWidth * rectHeight
            minArea = 30

            if blobArea > minArea:

                # Get image dimensions:
                (imageHeight, imageWidth) = localImage.shape[:2]

                # Set an empty array, this will be the binary mask
                shadowMask = np.zeros((imageHeight, imageWidth, 3), np.uint8)
                color = (255, 255, 255)
                cv2.rectangle(shadowMask, (int(rectX), int(0)),
                          (int(rectX + rectWidth), int(0 + imageHeight)), color, -1)
                # Invert mask:
                shadowMask = 255 - shadowMask

                # Store mask and cropped image:
                shadowRois.append((shadowMask.copy(), localImage.copy()))

好的,我根据这些信息创建了一个蒙版,其中唯一用白色绘制的就是蒙版的位置。我将这个掩码和原始的BGR 裁剪存储在shadowRois 列表中。

以下是使用此信息并创建完整图像的可能方法。这个想法是我使用每个蒙版的信息来复制所有非蒙版像素。我在缓冲区中积累这些信息,最初是一个空图像,如下所示:

# Prepare image buffer:
buffer = np.zeros((100, 100, 3), np.uint8)

# Loop through cropped images and produce the final image:
for r in range(len(shadowRois)):

    # Get data from the list:
    (mask, img) = shadowRois[r]
    # Get image dimensions:
    (imageHeight, imageWidth) = img.shape[:2]

    # Resize the buffer:
    newSize = (imageWidth, imageHeight)
    buffer = cv2.resize(buffer, newSize, interpolation=cv2.INTER_AREA)

    # Get the image mask:
    temp = cv2.bitwise_and(img, mask)

    # Set info in buffer, substitute the black pixels
    # for the new data:
    buffer = np.where(temp == (0, 0, 0), buffer, temp)

    cv2.imshow("Composite Image", buffer)
    cv2.waitKey(0)

结果是这样的:

【讨论】:

  • 这个答案也令人难以置信,我真的很感激。非常感谢@stateMachine。
  • 由于您的努力,我决定选择您的答案作为此问题的正确答案。但是 fmw42 建议的方法对我来说真的很好。谢谢@stateMachine。
猜你喜欢
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 2022-10-14
  • 2018-03-20
  • 2019-12-31
  • 1970-01-01
  • 2013-10-22
相关资源
最近更新 更多