如何使用多个图像制作单个图像?
How to make a single image using several images?
我有这些图像,所有图像中都有阴影。我的目标是使用这三张图片制作一张没有阴影的汽车图片:
最后,如何得到如下图这样的图片:
欢迎提供任何帮助或建议。
已编辑
根据评论,我使用了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)
这是一个可能的解决方案。总体思路是计算阴影的位置,生成识别阴影位置的二进制掩码,并使用此信息从所有裁剪的子图像中复制像素。
让我们看看代码。第一个问题是定位这三个图像。我使用黑框来分割和裁剪每辆车,像这样:
# 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
颜色 space 中可见,特别是在 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)
结果是这样的:
我有这些图像,所有图像中都有阴影。我的目标是使用这三张图片制作一张没有阴影的汽车图片:
最后,如何得到如下图这样的图片:
欢迎提供任何帮助或建议。
已编辑
根据评论,我使用了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)
这是一个可能的解决方案。总体思路是计算阴影的位置,生成识别阴影位置的二进制掩码,并使用此信息从所有裁剪的子图像中复制像素。
让我们看看代码。第一个问题是定位这三个图像。我使用黑框来分割和裁剪每辆车,像这样:
# 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
颜色 space 中可见,特别是在 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)
结果是这样的: