【问题标题】:Trim (noised) whitespaces in images using Python使用 Python 修剪(噪声)图像中的空白
【发布时间】:2019-04-08 06:35:56
【问题描述】:

假设我有一张如下图:

如您所见,除了一个大的绿色正方形(正方形随机放置在左下角)之外,图像还包含空白/白色空间(带有小绿点的噪点)。 我想要做的是裁剪图像/修剪白色空间,最后只得到大的绿色方块。
感谢 Stackoverflow 社区,我找到了一种修剪空白的方法(如果它不包含噪音)in this answer;这是我使用的代码:

from PIL import Image, ImageChops

def trim(im):
    bg = Image.new(im.mode, im.size, im.getpixel((0,0)))
    diff = ImageChops.difference(im, bg)
    diff = ImageChops.add(diff, diff, 2.0, -100)
    diff.show()
    bbox = diff.getbbox()
    if bbox:
        return im.crop(bbox)

im = Image.open("noised.jpg")
im = trim(im)
im.save('output.png')

由于空白/白色空间包含噪声,我得到的裁剪图像如下所示:

所以脚本在遇到第一个非白色(噪声)像素时裁剪了图像。
我的问题是:有没有办法在不影响图像(绿色方块)的情况下去噪空白区域,或者即使有噪声也可以直接修剪空白区域?
感谢您的帮助!

【问题讨论】:

    标签: python opencv python-imaging-library


    【解决方案1】:

    我的建议是纯粹使用 OpenCV 和 Numpy 库。

    假设您的原始图像具有“input_green.jpg”的文件名。该方法基于轮廓检测算法。

    首先,导入并加载输入图像,制作相关的灰度图像对象。

    import cv2
    import numpy as np
    img = cv2.imread("input_green.jpg", 1)
    image_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    

    其次,应用图像模糊和二值化方法,然后检测任何轮廓。

    image_gray = 255 - cv2.GaussianBlur(image_gray, (3,3), 0)
    # clean the image based on gray value
    image_gray = np.where(image_gray <= 50, 0, image_gray)
    image_gray = np.where(image_gray > 50, 255, image_gray)
    _,image_gray = cv2.threshold(image_gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    # draw a bounding box around the whole image
    h, w = image_gray.shape[:2]
    cv2.rectangle(image_gray, (0,0), (w, h), 0, 5)
    

    第三,按区域对轮廓进行检测和排序

    _,contours,_ = cv2.findContours(image_gray,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
    contours_sorted = sorted(contours, key=cv2.contourArea, reverse=True)
    

    最后,将最大的轮廓作为图像上的绿色矩形,从原始图像中裁剪出来。

    green_rect = contours_sorted[0]
    img_copy = img.copy()
    cv2.drawContours(img_copy, [green_rect], 0, (0, 0, 255), 2)
    cv2.imshow("result", img_copy)
    #cv2.imwrite("result_green_rect.jpg", img_copy)
    cv2.waitKey()
    

    临时结果:

    # crop the green rect
    (x, y, w, h) = cv2.boundingRect(green_rect)
    crop_result = img[y:y+h, x:x+w]
    cv2.imshow("crop_result", crop_result)
    #cv2.imwrite("result_green_rect_crop.jpg", crop_result)
    cv2.waitKey()
    

    裁剪结果:

    【讨论】:

    • 问题是当图像(这里是绿色方块)没有轮廓时。所以在这种情况下,这个方法是行不通的。
    • 不确定绿色方块何时没有轮廓。你用其他图片测试过我的答案吗?
    • 我用其他图像测试了你的代码(它们的轮廓不如绿色方块清晰),但效果不佳。
    • 那么这个答案还不够笼统……可能需要调整的参数很少。你介意发一个吗?
    • imgur.com/AMv3v02 这是我正在使用的图像的一个示例。我尝试了您提供的代码,但它不起作用,因为图像没有清晰的轮廓。
    【解决方案2】:

    这可以通过 OpenCV 来完成,方法是检测绿色区域,然后挑选出最大的区域。有几种方法可以检测绿色区域,包括cv2.inRangecv2.threshold

    1。识别绿色区域

    cv2.inRange

    使用inRange,您可以识别一定范围内的颜色。例如:

    lower_bound = (0,100,0)
    upper_bound = (10,255,10)
    

    因此可以识别颜色介于lower_boundupper_bound 之间的像素以创建带有

    的蒙版
    mask = cv2.inRange(im, lower_bound, upper_bound)
    

    这是绿色区域的mask

    cv2.threshold

    同样,阈值化将创建绿色区域的蒙版。首先,将图像转为灰度。

    imgray = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
    

    但是,如果您对该图像进行阈值处理,它将识别出白色区域,因此我们希望得到 cv2.THRESH_BINARY_INV 找到的阈值的倒数

    ok, thresh = cv2.threshold(imgray, 200, 255, cv2.THRESH_BINARY_INV)
    

    这是阈值图像:

    可以看出,threshmask 标识绿色区域。

    2。轮廓

    一旦我们有了掩码或阈值图像,我们就可以通过寻找轮廓来识别白色区域。我将使用mask 图像(thresh 也可以使用)。

    (im2, contours, hierarchy)  = cv2.findContours(mask, cv2.RETR_EXTERNAL,
                                    cv2.CHAIN_APPROX_SIMPLE)
    

    我们特别想要contours,它提供了一组可用于勾勒绿色区域的点。我们想要创建最大轮廓区域的轮廓,我们可以通过首先将它们从大到小排列,然后取第一个来找到它。

    ordered_cnts = sorted(contours, key=cv2.contourArea, reverse=True)
    largest_cnt = ordered_cnts[0]
    

    largest_cnt 是以下一组点:

    [[[  0 701]]
     [[  0 999]]
     [[298 999]]
     [[298 701]]
     [[289 701]]
     [[288 702]]
     [[287 701]]]
    

    这些点可用于勾勒图像左下角的绿色框。我们只需要一个矩形,因此我们可以通过找到包围所有这些点的最小矩形来勾勒整个轮廓。

    rect = cv2.minAreaRect(largest_cnt)
    box = cv2.boxPoints(rect)
    

    box 给出了rect 四个角的点列表。我们可以使用numpy 转换为整数点,并获取框的限制来裁剪图像。

    box = np.array(box, dtype=int)
    x0, y0 = np.min(box,axis=0)
    x1, y1 = np.max(box,axis=0)
    crop = im[y0:y1, x0:x1]
    

    crop 图片:

    组合代码

    lower_bound = (0,100,0)
    upper_bound = (10,255,10)
    mask = cv2.inRange(im, lower_bound, upper_bound)
    (im2, cnts, hierarchy) = cv2.findContours(mask, cv2.RETR_EXTERNAL,
                                        cv2.CHAIN_APPROX_SIMPLE)
    ordered_cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
    largest_cnt = ordered_cnts[0]
    rect = cv2.minAreaRect(largest_cnt)
    box = cv2.boxPoints(rect)
    box = np.array(box, dtype=int)
    x0, y0 = np.min(box,axis=0)
    x1, y1 = np.max(box,axis=0)
    crop = im[y0:y1, x0:x1]
    

    【讨论】:

    • 问题是当图像(这里是绿色方块)没有轮廓时,所以在这种情况下,这种方法不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 2012-11-23
    • 2018-11-08
    • 2013-10-27
    • 2011-12-20
    相关资源
    最近更新 更多