如何从图片中去除黑色部分?

13

我使用OpenCV函数和C++将两张图像拼接在一起,现在遇到一个问题,最终的图像包含了大片黑色部分。

最终的图像应该是一个只包含有效部分的矩形。我的图像如下:

enter image description here

如何去除黑色部分?


你可以使用cvFindContours来获取图像内容的范围,然后进行裁剪... - LiMuBei
2
在添加问题图片时,使用imwrite输出结果并添加。这比添加截图更好,因为回答你的问题的人需要从图像中删除不必要的区域。 - Abid Rahman K
4
你的工作空间有点整洁。 - wengseng
1
您想如何移除黑色部分?如果新图像的左上角与当前位置相同,而右下角位于拼接图像的最低点,这样可以吗?您是想将它裁剪成包含最少量黑色部分但仍包含所有图像部分,还是相反,裁剪掉最少量图像部分同时移除所有黑色部分?请详细说明 :) - penelope
3个回答

24

mevatron的回答是一种方法,可以最小化黑色区域的数量同时保留完整图像。

另一个选项是删除完全的黑色区域,这样会损失部分图像,但结果将是一个整洁的矩形图像。以下是Python代码:

在这里,您可以找到图像的三个主要角落,如下所示:

enter image description here

我标记了那些值。(1,x2), (x1,1), (x3,y3)。它基于假设您的图像从(1,1)开始。

代码:

前几步与mevatron相同。模糊图像以消除噪声,阈值图像,然后查找轮廓。

import cv2
import numpy as np

img = cv2.imread('office.jpg')
img = cv2.resize(img,(800,400))

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray,3)

ret,thresh = cv2.threshold(gray,1,255,0)
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

现在要找到您的图像中最大的轮廓。这是为了避免噪音的影响,以防有任何干扰(虽然很可能没有)。或者您可以使用mevatron的方法。

max_area = -1
best_cnt = None

for cnt in contours:
    
    area = cv2.contourArea(cnt)
    if area > max_area:
        max_area = area
        best_cnt = cnt

现在,对轮廓进行近似处理以去除不必要的点,但仍保留所有角点值。

approx = cv2.approxPolyDP(best_cnt,0.01*cv2.arcLength(best_cnt,True),True)

现在我们要找到角落。

首先,我们要找到(x3,y3)。它是最远的点。因此,x3*y3将非常大。因此,我们找到所有点对的乘积并选择乘积最大的一对。

far = approx[np.product(approx,2).argmax()][0]

Next (1,x2)。这是第一个元素为一,第二个元素为最大值的点。

ymax = approx[approx[:,:,0]==1].max()

下一个 (x1,1)。这是第二个元素为1的点,然后第一个元素是最大值。

xmax = approx[approx[:,:,1]==1].max()

现在我们要找到在 (far.x, xmax) 和 (far.y, ymax) 中的 最小值

x = min(far[0],xmax)
y = min(far[1],ymax)

如果你用 (1,1) 和 (x,y) 绘制一个矩形,你会得到下面的结果:

enter image description here

因此,你需要裁剪图像以获得正确的矩形区域。

img2 = img[:y,:x].copy()

以下是结果:

在此输入图像描述

问题在于您会丢失一些拼接图像的部分。


1
由于我更熟悉C++,尝试将Python代码转换为C++,但并没有取得太大的成功。如果您能够用C++语言分享相同的代码,我将不胜感激。 - deepak_k
我已经完成了大约到approxpolyDP的所有步骤,但是我不知道如何在C中计算坐标点的乘积,而且我认为您也使用了伪代码。您能告诉我如何在C中计算坐标点的乘积,或者给我任何链接,在那里有人使用了您这种方法的C语言实现。参考我的图像应用approxpoly()方法后是[http://i.imgur.com/Qfoyt.jpg?1]。希望您在应用approxpolyDp()之后也得到了相同的输出图像。 - deepak_k
好的。但是,我认为在C语言中,我们不能像这样获取坐标。你能告诉我其他的方法吗?我在StackOverflow上看到霍夫变换被用来获取线条,但我认为那会不必要地增加复杂度。你知道其他的方法吗? - deepak_k
@AbidRahmanK 你有使用过Matlab吗?我在寻找与cv2.approxPolyDP()cv2.arcLength()相对应的函数时遇到了麻烦。你的方法正是我需要的,但是在这些函数上卡住了。请看一下我的问题,链接在这里http://stackoverflow.com/questions/35170367/how-to-find-black-patch-in-an-image-in-matlab - Nancy
谢谢,这个很好用。(@deepak_k - 如果你需要将代码翻译成c++,请告诉我)。@Abid 你认为重新拼接这个裁剪后的图像和源图像以恢复一些丢失的细节是一个好主意吗?还是有其他方法? - ThunderWiring
显示剩余5条评论

9
您可以使用 thresholdfindContoursboundingRect 来完成此操作。下面是一个使用 Python 接口快速进行此操作的示例脚本。
stitched = cv2.imread('stitched.jpg', 0)
(_, mask) = cv2.threshold(stitched, 1.0, 255.0, cv2.THRESH_BINARY);

# findContours destroys input
temp = mask.copy()
(contours, _) = cv2.findContours(temp, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# sort contours by largest first (if there are more than one)
contours = sorted(contours, key=lambda contour:len(contour), reverse=True)
roi = cv2.boundingRect(contours[0])

# use the roi to select into the original 'stitched' image
stitched[roi[1]:roi[3], roi[0]:roi[2]]

最终结果如下所示: 在此输入图片描述 注意:在原始图像中可能不需要排序,但是使用压缩图像在低阈值情况下会出现一些压缩伪影,因此我进行了后处理并进行了排序。
希望这有所帮助!

0

您可以使用主动轮廓(气球/蛇线)精确选择黑色区域。您可以在这里找到演示链接。OpenCV中提供了主动轮廓,请查看cvSnakeImage


你能展示一下如何实现移除黑色部分的功能吗? - deepak_k
你添加的链接都已失效。最好展示并解释一下实现方法,这样你的回答就会更加具有时效性。 - Optimus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接