我的目標是在將影像轉換為 numpy 陣列后從影像中洗掉深色的水平線和垂直線。我不想為此使用預定義的影像模塊,因為我想對閾值等引數進行精細控制。
我的邏輯如下:
- 使用 .將彩色影像轉換為 3D numpy 陣列
image(BGR)cv2.imread。 - 迭代行索引并使用
row = image[row_index,:,:]. - 在每一行中,根據所有 3 個通道值是否低于定義的閾值,計算有多少像素是“黑色像素”。
- 如果一行中有足夠數量(或比率)的像素滿足上述條件,則將此行索引存盤到串列中
remove_rows。 preserve_rows在所有迭代之后,根據 list確定要保留、存盤到 中的行remove_rows。- 行洗掉后的新影像可以估計為
image = image[preserve_rows,:,:]。 - 對列也重復該程序。
該程式有效,但需要很長時間。我認為時間復雜度是O(rows * columns * 3)因為每個值都必須被訪問并與閾值進行比較。該程式需要大約 9 秒處理單個影像,這是不可接受的,因為我最終計劃在函式中使用該函式在 Keras 中進行預處理,ImageDataGenerator并且我不確定該函式在神經網路訓練期間是否使用 GPU。完整代碼如下:
def edge_removal(image, threshold=50, max_black_ratio=0.7):
num_rows, _, _ = image.shape
remove_rows = []
threshold_times = []
start_time = time.time()
for row_index in range(num_rows):
row = image[row_index,:,:]
pixel_count = 0
black_pixel_count = 0
for pixel in row:
pixel_count = 1
b,g,r = pixel
pre_threshold_time = time.time()
if all([x<=threshold for x in [b,g,r]]):
black_pixel_count = 1
threshold_times.append(time.time()-pre_threshold_time)
if pixel_count > 0 and (black_pixel_count/pixel_count)>max_black_ratio:
remove_rows.append(row_index)
time_taken = time.time() - start_time
print(f"Time taken for thresholding = {sum(threshold_times)}")
print(f"Time taken till row for loop = {time_taken}")
preserve_rows = [x for x in range(num_rows) if x not in remove_rows]
image = image[preserve_rows,:,:]
_, num_cols, _ = image.shape
remove_cols = []
for col_index in range(num_cols):
col = image[:,col_index,:]
pixel_count = 0
black_pixel_count = 0
for pixel in col:
pixel_count = 1
b,g,r = pixel
if all([x<=threshold for x in [b,g,r]]):
black_pixel_count = 1
if pixel_count > 0 and (black_pixel_count/pixel_count)>max_black_ratio:
remove_cols.append(col_index)
preserve_cols = [x for x in range(num_cols) if x not in remove_cols]
image = image[:,preserve_cols,:]
time_taken = time.time() - start_time
print(f"Total time taken = {time_taken}")
return image
代碼的輸出是:
Time taken for thresholding = 3.586946487426758
Time taken till row for loop = 4.530229091644287
Total time taken = 8.74315094947815
我嘗試了以下方法:
- 使用多執行緒替換外部 for 回圈,其中執行緒函式的引數是執行緒號(執行緒數 = 影像中的行數)。但是,這并沒有加快程式的速度。這可能是因為 for 回圈是一個受 CPU 限制的行程,由于全域解釋器鎖而無法加速,如此 SO answer 所述。
- 尋找其他建議如何降低程式的時間復雜度。這個答案對我沒有多大幫助,因為從輸出中可以看出,瓶頸不是洗掉。執行閾值的比較次數是減慢該程式速度的原因。
有什么建議或啟發式方法可以減少計算量,從而減少程式的處理時間?
uj5u.com熱心網友回復:
由于您的代碼由執行相同作業的兩個部分組成,只是在影像的不同維度上,我將所有邏輯移動到一個函式中,該函式告訴您“像素系列”(行或列,無關緊要) ) 提供高于或低于閾值。
我用電話代替了所有的手動計數len。
各種生成器 ( r, g, b = pixel; x <= threshold for x in (r, g, b)) 被直接 numpy 陣列比較替換pixel <= threshold,pythonall被替換為 numpy .all()。
新舊代碼分別在 5.9 秒和 37 毫秒內處理我的測驗影像,并增加了可讀性。
def edge_removal(image, threshold=50, max_black_ratio=0.7):
def pixels_should_be_conserved(pixels) -> bool:
black_pixel_count = (pixels <= threshold).all(axis=1).sum()
pixel_count = len(pixels)
return pixel_count > 0 and black_pixel_count/pixel_count <= max_black_ratio
num_rows, num_columns, _ = image.shape
preserved_rows = [r for r in range(num_rows) if pixels_should_be_conserved(image[r, :, :])]
preserved_columns = [c for c in range(num_columns) if pixels_should_be_conserved(image[:, c, :])]
image = image[preserved_rows,:,:]
image = image[:,preserved_columns,:]
return image
為了進一步解釋為我們節省最多時間的更改(計算黑色像素),讓我們看一個簡化的示例。
red = np.array([255, 0, 0])
black = np.array([0, 0, 0])
pixels = np.array([red, red, red, black, red]) # Simple line of 5 pixels.
threshold = 50
pixels <= threshold
# >>> array([[False, True, True],
# [False, True, True],
# [False, True, True],
# [True, True, True],
# [False, True, True]])
(pixels <= threshold).all(axis=1)
# >>> array([False,
# False,
# False,
# True,
# False])
# We successfully detect that the fourth pixel has all its rgb values below the threshold.
(pixels <= threshold).all(axis=1).sum()
# >>> 1
# Summing a boolean area is a handy way of counting how many element in the
# array are true, i.e. dark enough in our case.
備選方案 1:HSV。
我們可以考慮的另一件事是使用 HSV 顏色系統,因為您只擔心問題中的亮度。這將允許使用檢查s <= threshold每個像素,所以一個比較而不是三個。
盡管兩次與 HSV 之間的轉換,影像的處理時間為 18 毫秒而不是 37 毫秒。
def edge_removal(image, threshold=50, max_black_ratio=0.7):
def pixels_should_be_conserved(pixels) -> bool:
black_pixel_count = (pixels[:,2] <= threshold).sum() # Notice the change here.
pixel_count = len(pixels)
return pixel_count > 0 and black_pixel_count/pixel_count <= max_black_ratio
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
num_rows, num_columns, _ = image.shape
preserved_rows = [r for r in range(num_rows) if pixels_should_be_conserved(image[r, :, :])]
preserved_columns = [c for c in range(num_columns) if pixels_should_be_conserved(image[:, c, :])]
image = image[preserved_rows,:,:]
image = image[:,preserved_columns,:]
image = cv2.cvtColor(image, cv2.COLOR_HSV2BGR)
return image
備選方案 2:灰度。
我們還可以在灰度模式下作業,并將“像素”(現在是單個值)直接與閾值進行比較。與 HSV 替代方案相比,我們節省了一次轉換,但使用了更多記憶體。
它在 14 毫秒內運行。
def edge_removal(image, threshold=50, max_black_ratio=0.7):
def pixels_should_be_conserved(pixels) -> bool:
black_pixel_count = (pixels <= threshold).sum()
pixel_count = len(pixels)
return pixel_count > 0 and black_pixel_count/pixel_count < max_black_ratio
image_grayscale = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
num_rows, num_columns, _ = image.shape
preserved_rows = [r for r in range(num_rows) if pixels_should_be_conserved(image_grayscale[r, :])]
preserved_columns = [c for c in range(num_columns) if pixels_should_be_conserved(image_grayscale[:, c])]
image = image[preserved_rows,:,:]
image = image[:,preserved_columns,:]
return image
基準測驗:
| RGB (OP) | RGB | 單純皰疹病毒 | 灰色的 |
|---|---|---|---|
| 5900 毫秒 | 37 毫秒 | 18 毫秒 | 14 毫秒 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/461728.html
上一篇:從位元組加載numpy中的npz
