我有一個包含黑色像素的影像。它可以是垂直線,也可以是簡單的點。我想用相鄰像素(左右)的平??均值替換這些像素。
黑色像素的左右鄰居都具有與黑色不同的值。

現在我有這個:
import numpy as np
from matplotlib import pyplot as plt
import time
#Creating test img
test_img = np.full((2048, 2048, 3), dtype = np.uint8, fill_value = (255,127,127))
#Draw vertical black line
test_img[500:1000,1500::12] = (0,0,0)
test_img[1000:1500,1000::24] = (0,0,0)
#Draw black point
test_img[250,250] = (0,0,0)
test_img[300,300] = (0,0,0)
#Fill hole functions
def fill_hole(img):
#Find coords of black pixek
imggray = img[:,:,0]
coords = np.column_stack(np.where(imggray < 1))
print(len(coords))
#Return if no black pixel
if len(coords) == 0:
return img
percent_black = len(coords)/(img.shape[0]*img.shape[1]) * 100
print(percent_black)
#Make a copy of input image
out = np.copy(img)
#Iterate on all black pixels
for p in coords:
#Edge management
if p[0] < 1 or p[0] > img.shape[0] - 1 or p[1] < 1 or p[1] > img.shape[1] - 1:
continue
#Get left and right of each pixel
left = img[p[0], p[1] - 1]
right = img[p[0], p[1] 1]
#Get new pixel value
r = int((int(left[0]) int(right[0])))/2
g = int((int(left[1]) int(right[1])))/2
b = int((int(left[2]) int(right[2])))/2
out[p[0],p[1]] = [r,g,b]
return out
#Function call
start = time.time()
img = fill_hole(test_img)
end = time.time()
print(end - start)
此代碼在我的示例中運行良好,但黑色像素串列上的回圈需要時間,具體取決于其大小。
有沒有辦法優化這個?
uj5u.com熱心網友回復:
一般來說,numpy 陣列上的 for 回圈通常會導致速度變慢,并且在大多數情況下可以通過 numpy 內置函式來避免。在你的情況下,考慮在影像上使用卷積,作為參考:

請注意,這只是一個討厭的、不準確的 JPEG 表示,因為原始影像對于 imgur 來說太大了。
然后我運行了這段代碼:
#!/usr/bin/env python3
import cv2
import numpy as np
# Load image 2048x2048 RGB
im = cv2.imread('paddington.png')
# Make mask of black pixels, True where black
blackMask = np.all(im==0, axis=-1)
cv2.imwrite('DEBUG-blackMask.png', (blackMask*255).astype(np.uint8))
# Convolve with [0.5, 0, 0.5] to set each pixel to average of its left and right neighbours
kernel = np.array([0.5, 0, 0.5], dtype=float).reshape(1,-1)
print(kernel.shape)
convolved = cv2.filter2D(im, ddepth=-1, kernel=kernel, borderType=cv2.BORDER_REPLICATE)
cv2.imwrite('DEBUG-convolved.png', convolved)
# Choose either convolved or original image at each pixel
res = np.where(blackMask[...,None], convolved, im)
cv2.imwrite('result.png', res)
結果是(又一個討厭的、調整大小的 JPEG):

時間安排在這里,并且可能會進一步改進 - 不確定您的代碼實作了哪些時間安排或您需要什么:
In [55]: %timeit blackMask = np.all(im==0, axis=-1)
22.3 ms ± 29.1 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [56]: %timeit convolved = cv2.filter2D(im, ddepth=-1, kernel=kernel, borderType=cv2.BORDER_REPLICATE
...: )
2.66 ms ± 3.07 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [57]: %timeit res = np.where(blackMask[...,None], convolved, im)
22.7 ms ± 76.2 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
所以大約 46ms intoto。請注意,您可以注釋掉所有創建輸出影像的行,DEBUG-xxx.png因為它們僅用于除錯并以這樣的方式命名,因此我可以在測驗后輕松清理。
我認為這會很好地運行,numba但目前llvmlite我的 M1 Mac 不支持,所以我不能嘗試。這是與numba.
優化
我考慮了對上面代碼的一些優化。似乎有兩個方面比它們可能要慢 - 制作掩碼并將卷積值插入陣列。所以,首先看制作面具,我最初是這樣做的:
blackMask = np.all(im==0, axis=-1)
這花了 22 毫秒。我這樣嘗試過numexpr:
import numexpr as ne
R=im[...,0]
G=im[...,1]
B=im[...,2]
blackMask = ne.evaluate('(R==0)&(G==0)&(B==0)')
這得到了相同的結果,但只需要 1.88 毫秒而不是 22 毫秒,因此節省了 20 毫秒。
我正在尋找其他想法...
uj5u.com熱心網友回復:
您總是對黑色像素應用相同的操作。所以它是高度可并行化的。將您的影像劃分為較小的矩形,并在每個小矩形上放置執行緒和/或行程。您可以嘗試調整矩形大小以獲得最佳性能。
此外,由于您的黑色像素結構非常特殊,您可以實施一些策略來避免檢查您確定沒有黑色像素的某些影像區域。一個想法可以是為每個影像列檢查列的開頭、中間和結尾的一些隨機像素。然后,如果您在檢查中沒有發現黑色像素,請忽略該列。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/463189.html
