以前在做天空偵測的時候也類似目前的 case,也就是呢,我們要先分析客戶給的信號圖,在進行必要的裁剪、銳化細節、canny 邊緣檢測,在跟據最後 canny 的圖將信號畫好,(至於說為啥我要做那些步驟,阿就試出來的,我最愛做的就是 try error 這種繁瑣的事情)
設計流程
以這張圖為例,我們可以看到上下方約 1/3 都是我們不需要的東西,所以我們進行 crop image 的動作 。
再來就是那些該死的示波器背景,等等我們就用 blur 把它濾掉,因為輸入屬於中型圖片,所以 blur 我們使用[5,5]的強度。
後面畫細節是由上到下,取最高最低兩個點,如果沒有發現像素點就會沿用前一條線高低點。當然這還是有改善空間,模糊不知道用高斯模糊效果好不好,還有很多可以試試看。但因為這件事貌似沒那麼急,就暫時先告一段落,如果有後續發展,會再繼續更新本篇。
調整出來步驟為
- step1: crop image 裁剪原圖
- step2: floodfill image 嘗試塗掉背景 <== 強度應該可以再調整
- step3: kernel image 銳化留下來的部分,’[[0, -1, 0], [-1, 5, -1], [0, -1, 0]’
- step4: blur image 抹掉細節(模糊),’[5,5]’ <== 或許可以用高斯模糊
- step5: canny image 100,200 邊緣檢測 <== sobel 展現的效果不好,所以還是用canny
- step6: result image 畫線
0QD8imz4.png
original(已移除)
(20200702移除)
canny
result
folder Tree
$tree
D:.
└─gw_signal_recovery
├─input
├─output
└─tmp
code
from os.path import isfile, join
from os import listdir
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from PIL import ImageFilter
import cv2 # if not found,try pip install opencv-python
import os
print("Change the current working directory to path:", os.path.dirname(__file__))
os.chdir(os.path.dirname(__file__))
onlyfiles = [f for f in listdir("./input") if isfile(join("./input", f))]
print(onlyfiles)
'''
3YljKfLQ.png
4B2Dcn2o.png
8dJosPug.png
8uS7qspI.png
buY6cs6U.png
Clqh9OD0.png
DH1QeME4.png
jyicdNJM.png
KsJUDLN0.png
m-GNRpbw.png
TO5-rs9o.png
u4J8dZ7o.png
VvCGeC4o.png
'''
# 將波形以外的背景填成白色
def floodfill_image(image, power):
copyImage = image.copy() # 複製原圖像
h, w = image.shape[:2] # 讀取圖像的寬和高
mask = np.zeros([h+2, w+2], np.uint8) # 新建圖像矩陣 +2是官方函數要求
cv2.floodFill(copyImage, mask, (517, 260), (255, 255, 255), (power,
power, power), (power, power, power), cv2.FLOODFILL_FIXED_RANGE)
#cv.imshow("floodFill", copyImage)
return copyImage
# 將canny最後結果填上白色
def result_image(image):
copyImage = image.copy()
rows, cols = copyImage.shape
tmp_top = 0
tmp_down = 0
# 圖片大小
# print(str(rows),",",str(cols))
for i in range(cols):
top = 0
down = rows
for j in range(rows):
k = copyImage[j, i]
if(k > 100):
if (j > top):
top = j
if (j < down):
down = j
if(down - top > rows/2):
cv2.line(copyImage, (i, tmp_top),
(i, tmp_down), (255, 255, 255), 1)
#print("top =" + str(tmp_top) +",down ="+str(tmp_down))
else:
cv2.line(copyImage, (i, top), (i, down), (255, 255, 255), 1)
tmp_top = top
tmp_down = down
return copyImage
def cv2_show_img(title, img, is_save):
cv2.imshow(title, img)
if(is_save):
cv2.imwrite("output\\" + title, img)
index = 0 #方便檔案總管排序
# === main ===
for file in onlyfiles:
src = cv2.imread("input\\" + file)
H, W, channels = src.shape
# 裁切區域的 x 與 y 座標(左上角)
x = 10
y = 90
# (右下角)
x1 = W-30
y1 = 545
crop_img = src[y:y1, x:x1]
cv2_show_img(str(index) + "_1_original_" + file , src, True)
#cv2.imshow("crop_img", crop_img)
# flood fill 邊緣偵測
ff_img = floodfill_image(crop_img, 35)
#cv2.imshow("floodfill", ff_img)
# blur 模糊
blur = cv2.blur(ff_img, (5, 5)) # 中型圖片
#cv2.imshow("blur", blur)
# kernel 銳化
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)
kernel_img = cv2.filter2D(blur, -1, kernel=kernel)
#cv2.imshow("kernel", kernel_img)
# canny 邊緣偵測
canny = cv2.Canny(kernel_img, 100, 200)
cv2_show_img(str(index) + "_2_canny_" + file, canny, True)
# sobel 邊緣偵測(未使用)
'''
Gx = cv2.Sobel(blur,cv2cv.CV_16S,1,0)
Gy = cv2.Sobel(blur,cv2.CV_16S,0,1)
absX = cv2.convertScaleAbs(Gx)
absY = cv2.convertScaleAbs(Gy)
dst = cv2.addWeighted(absX,0.5,absY,0.5,0)
cv2.imshow("absX", absX)
cv2.imshow("absY", absY)
cv2.imshow("sobel", dst)
'''
result_img = result_image(canny)
cv2_show_img(str(index) + "_3_result_" + file, result_img, True)
# 按任一鍵繼續下一張
cv2.waitKey(0)
cv2.destroyAllWindows()
index += 1
Result
3YljKfLQ.png
4B2Dcn2o.png
8dJosPug.png
8uS7qspI.png
buY6cs6U.png
Clqh9OD0.png
DH1QeME4.png
jyicdNJM.png
KsJUDLN0.png
m-GNRpbw.png
TO5-rs9o.png
u4J8dZ7o.png
VvCGeC4o.png