解決:我們選擇以HSV來做顏色的追蹤,以抓取紅框雜訊為主。
解決:採取將圖片灰階的方法,就沒有顏色的影響。
HSV(Hue, Saturation, Value)
HSV+OpenCV
# 將RGB轉換成HSV顏色空間
redhsv1 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
轉換成HSV後就可以來將特定的顏色找出來,助於我們找出紅框的顏色,加以去除。
首先,我們需要用到HSV的調色盤,才抓到圖檔每個點的值是多少。以下圖為例,我點選左右兩點的紅線,他則可以顯示出我們需要的HSV值為多少。
import cv2
import numpy as np
# 讀取中文路徑圖檔(圖片讀取為BGR)
def cv_imread(filePath):
cv_img = cv2.imdecode(np.fromfile(filePath, dtype=np.uint8), -1)
return cv_img
# 點擊欲判定HSV值的圖片位置(以滑鼠左鍵單擊)
def mouse_click(event, x, y, flags, para):
if event == cv2.EVENT_LBUTTONDOWN:
print("BGR:", img[y, x])
print("GRAY:", gray[y, x])
print("HSV:", hsv[y, x])
print('='*30)
if __name__ == '__main__':
# 讀取圖檔
img = cv_imread('./pic/1_經.jpg')
img = cv2.resize(img, (320, 240))
# 轉換成gray與HSV
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.namedWindow("img")
cv2.setMouseCallback("img", mouse_click)
while True:
cv2.imshow('img', img)
if cv2.waitKey() == ord('q'):
break
cv2.destroyAllWindows()
透過這樣點選的方式,可以找出紅線的值,我們就可以將它取出。我們先鎖定紅線,慢慢的點,大概有個閾值出來。下列程式碼為追蹤紅線。
def red1_mask(img):
#紅1
lower = np.array([150,80,94])
upper = np.array([180,255,255])
redhsv1 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask1 = cv2.inRange(redhsv1, lower, upper)
return mask1
白色是我們追蹤到的紅線部分,發現右邊還有些紅線尚未被追蹤到。
於是我們要試著把右邊紅線的部分,看能否追蹤到多一點。依照上述步驟繼續點選紅線位置,找出他的閾值,下列程式碼為追蹤第二段紅線。
def red2_mask(img):
#紅2
lower = np.array([0,80,89])
upper = np.array([10,255,255])
redhsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(redhsv, lower, upper)
return mask
可以發現我們追蹤到上面沒追蹤到的紅色部分,還缺少最右邊的紅色線條,因為他有跟中文字重疊。
接下來我們將追蹤到的兩個紅色線條合起來。
#紅1
mask1 = red1_mask(img)
#紅2
mask2 = red2_mask(img)
#紅1 + 紅2 範圍
mask3 = cv2.bitwise_or(mask1,mask2)
透過bitwise_or取聯集,使兩張圖合併在一起。
取出紅線之後,我們需要將他去除掉,也就是用背景顏色去填補,在這之前我們先將他膨脹,然後白色的地方轉成黑色的。
def my_dilate(img):
kernel = np.ones((3, 3), np.uint8)
new_img = cv2.dilate(img, kernel, iterations=1)
return new_img
# 膨脹mask3
mask3 = my_dilate(mask3)
#黑白反轉
mask3 = cv2.bitwise_not(mask3,mask3)
黑框是特別截圖下來,以示整張圖片,主要是白色變黑色。
先做一個動作,將原本的圖片做灰階,再來就是黑色部份我們到時候要用背景顏色來填補,所以我們要取出最常出現的顏色,也就是取眾數。以下程式碼。
def get_mode(img):
##閾值取眾數
# bincount():統計非負整數的個數,不能統計浮點數
counts = np.bincount(img.flatten())
#counts的index代表出現的數,counts[index]代表出現數的次數
#今要求counts[index] 排序後最大跟第二大的counts的index(代表眾數跟出現第二多次的數)
counts_sort = np.argsort(counts) #最後一個元素是counts最大值的index ,倒數第二是二大
index = counts_sort[-1]
#以防圖片出現大量黑色面積
if index <= 100: #出現大量黑色區塊的話,取第二多數
print('index2:',index)
index = counts_sort[-2]
return index
#否則就return原本的眾數
print('index_org',index)
return index
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
image_mode =get_mode(image)
#把紅色的mask區域換成眾數
image[mask3==0]= image_mode
這樣我們就成功把紅線的雜訊去除了。
這邊圖片還是有點模糊,我們做了高斯濾波,讓圖片較平滑,左邊是原圖,右邊是高斯濾波過後的圖,可以看出較平滑。
blur = cv2.GaussianBlur(image,(3,3),0)
這邊當時遇到了小問題,當時在選擇灰階和二質化的圖檔做訓練的時候,兩種我們都有嘗試過,但後來發現灰階的資訊較多,所以我們選擇用灰階的方式去訓練圖片。
二質化 灰階
import os
import random
import shutil
import os
import shutil
# 分成訓練集跟資料集
src_dir_name = './train/'
target_dir_name = './test/'
test_size = 0.3
labels = set(os.listdir(src_dir_name))
def word_classfier():
word_list_dir = []
for i in os.listdir(src_dir_name):
if i.endswith('.jpg'):
word_list_dir.append(i.split('.')[0][-1])
word_list_dir = set(word_list_dir)
print(word_list_dir)
for i in os.listdir(src_dir_name):
if i.endswith('.jpg'):
if i.split('.')[0][-1] in word_list_dir:
try:
os.mkdir(src_dir_name+i.split('.')[0][-1])
except FileExistsError:
pass
shutil.move(src_dir_name+i,src_dir_name+i.split('.')[0][-1]+'/'+i)
def move_test_data(test_data:list):
for i in test_data:
word_subfolder = i.split('.')[0][-1]
if word_subfolder in labels:
print(src_dir_name+word_subfolder+'/'+i)
try:
os.mkdir(target_dir_name +word_subfolder)
except FileExistsError:
pass
shutil.move(src_dir_name+word_subfolder+'/'+i,target_dir_name +word_subfolder+'/'+i)
elif word_subfolder not in labels:
word_subfolder = i.split('.')[0][0]
print(src_dir_name+word_subfolder+'/'+i)
try:
os.mkdir(target_dir_name +word_subfolder)
except FileExistsError:
pass
try:
shutil.move(src_dir_name+word_subfolder+'/'+i,target_dir_name +word_subfolder+'/'+i)
except FileNotFoundError:
shutil.move(src_dir_name + word_subfolder + '/' + i, target_dir_name + word_subfolder + '/' + i)
def test_train_split():
try:
os.mkdir(target_dir_name)
except FileExistsError:
pass
for i in os.listdir(src_dir_name):
#每個字的照片數
dir_length = len(os.listdir(src_dir_name+i))
#3:7抽樣
test_size = round(0.3 * dir_length)
test_data = random.sample(os.listdir(src_dir_name+i), k=test_size)
move_test_data(test_data)
if __name__ == '__main__':
# 把字分類成800個資料夾
word_classfier()
# 分成訓練集跟測試集
test_train_split()