Python:快速删除图像中的水平黑线

3
我想要在一张图片上去除水平的黑线:

enter image description here

为了做到这一点,我会插值每个像素列的RGB值。
黑线消失了,但我认为可以优化这个函数。
def fillMissingValue(img_in):
    img_out = np.copy(img_in)

    #Proceed column by column
    for i in tqdm(range(img_in.shape[1])):
        col = img_in[:,i]

        col_r = col[:,0]
        col_g = col[:,1]
        col_b = col[:,2]
        r = interpolate(col_r)
        g = interpolate(col_g)
        b = interpolate(col_b)
        img_out[:,i,0] = r
        img_out[:,i,1] = g
        img_out[:,i,2] = b

    return img_out

def interpolate(y):
    x = np.arange(len(y))
    idx = np.nonzero(y)
    interp = interp1d(x[idx],y[idx],fill_value="extrapolate" )

    return interp(x)

if __name__ == "__main__":
    img = cv2.imread("lena.png")
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    img = cv2.resize(img, (1024,1024))


    start = time.time()
    img2 = fillMissingValue(img)
    end = time.time()

    print("Process time: {}".format(np.round(end-start,3)))

你有什么想法吗? 我考虑通过识别黑线的位置来进行预处理步骤。因此只插值相邻像素。但我认为这不会更快。

当前结果:

enter image description here


3
请问您能否发布没有外部刻度标记和标签的原始图像? - fmw42
1
虽然不是很重要,但我认为在“插值”中,对于给定图像的大小,“x”在所有调用中都是相同的,因此每次无需重新计算它(并分配新数组)。 - Dan Mašek
1
我们能假设这些线是真正的水平线,且从边缘到边缘延伸吗?如果是这样,我们还可以为整个图像一次性计算idx……然后通过一些重塑,在单个对interp1d的调用中完成整个操作。快速原型:https://pastebin.com/zHxYmdjx -- 变体 3 的时间只需要你的六分之一(不带进度条)。 - Dan Mašek
你的代码有些混淆不清。你使用np.nonzero来区分图片和黑色线条(零)。 - Christoph Rackwitz
1
关于不同的解决方案...修补应该可以很好地工作。或者检测每条黑线,然后取相邻的两条非黑线(检查它们是否为非黑色!)并计算平均值并分配。 - Christoph Rackwitz
由于您将示例输入保存为JPEG格式,这是有损压缩的,因此这些线条不再是纯黑色,而常常是深红色或紫罗兰色 - 红色的强度至少高达48。不确定是否有意为之(PNG可能是更好的选择)。请澄清。 - Dan Mašek
1个回答

2

interp1d 不是非常高效。

正如@ChristophRackwitz在评论中提出的那样,您可以检测行的位置并使用OpenCV提供的修复方法:

img = cv2.imread('lena.jpg')

# Locate the relatively black lines
threshold = 25
lineIdx = np.where(np.mean(img, axis=(1,2)) < threshold)

# Perform inpainting on the located lines
mask = np.zeros(img.shape[:2], dtype=np.uint8)
mask[lineIdx] = 255

# Actual inpainting.
# Note: using 2 or 1 instead of 3 make the computation 
# respectively ~2 and ~4 time faster on my machine but 
# the result is not as beautiful with 3.
img2 = cv2.inpaint(img, mask, 3, cv2.INPAINT_NS)

在我的机器上,计算部分需要87毫秒,而您的代码需要342毫秒。请注意,由于JPEG压缩,结果并不是很好。您可以修复邻近的行(例如lineIdx-1lineIdx+1),以便以更慢的计算速度获得更好的结果(在我的机器上大约慢2.5倍)。


另一种解决方案是在Numpy中自己执行插值:

%%time
# Locate the relatively black lines
threshold = 25
lineIdx = np.where(np.mean(img, axis=(1,2)) < threshold)[0]
lineIdxSet = set(lineIdx)

img2 = img.copy()
start, end = None, None
interpolate = False
for i in range(img.shape[0]+1):
    if i in lineIdxSet:
        if start is None:
            start = i
        end = i
    else:
        if not (start is None):
            assert not (end is None)
            # The first lines are black
            if start <= 0:
                i0, i1 = end+1, end+1
            # The last lines are black
            elif end >= img.shape[0]-1:
                i0, i1 = start-1, start-1
            # usual case
            else:
                i0, i1 = start-1, end+1
            # The full image is black
            if i0 < 0 or i1 >= img.shape[0]:
                continue
            end = min(end, img.shape[0]-1)
            # Actual linear interpolation (of a bloc of lines)
            coef = np.linspace(0, 1, end-start+3)[1:-1].reshape(-1, 1)
            l0 = img[i0].reshape(-1)
            l1 = img[i1].reshape(-1)
            img2[start:end+1] = (coef * l0 + (1.0-coef) * l1).reshape(-1, img.shape[1], 3)
        start, end = None, None

这段代码在我的机器上仅需5毫秒。结果应该与您原来的代码类似,不同之处在于它是逐行而不是逐列工作,并且检测对于每个颜色通道不是独立的。请注意,当大块的线条为黑色时,修补方法会产生更美丽的结果。

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