文章目錄
- 背景
- 影像預處理
- 確定字符區域
- 分割單個字符
- 完整原始碼
- 結語
背景
在諸如車牌識別,數字儀表識別等問題中,最關鍵的就是將單個的字符分割開來再分別進行識別,如下圖,最近剛好用到,就自己寫了一個簡單地演算法進行字符分割,來記錄一下,

影像預處理
彩圖二值化以減小引數量,再進行腐蝕膨脹去除噪點,
image = cv2.imread('F://demo.jpg', 0) # 讀取為灰度圖
_, image = cv2.threshold(image, 50, 255, cv2.THRESH_BINARY) # 二值化
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) # 腐蝕膨脹核
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 腐蝕膨脹核
image = cv2.erode(image, kernel=kernel1) # 腐蝕
image = cv2.dilate(image, kernel=kernel2) # 膨脹

確定字符區域
考慮最理想的情況,圖中的字符是端正沒有傾斜歪曲的,將像素灰度矩陣分別進行列相加、行相加,則在得到的列和、行和陣列中第一個非 0 元素索引到最后一個非 0 元素索引包裹的區間即就是字符區域,
h, w = image.shape # 原圖的高和寬
list1 = [] # 列和
list2 = [] # 行和
for i in range(w):
list1.append(1 if image[:, i].sum() != 0 else 0) # 列求和,不為0置1
for i in range(h):
list2.append(1 if image[i, :].sum() != 0 else 0) # 行求和,不為0置1
# 裁剪字符區域
# 求行的范圍
flag = 0
for i, e in enumerate(list1):
if e != 0:
if flag == 0: # 第一個不為0的位置記錄
start_w = i
flag = 1
else: # 最后一個不為0的位置
end_w = i
# 求列的范圍
flag = 0
for i, e in enumerate(list2):
if e != 0:
if flag == 0: # 第一個不為0的位置記錄
start_h = i
flag = 1
else: # 最后一個不為0的位置
end_h = i
print(start_w, end_w) # 行索引范圍
print(start_h, end_h) # 列索引范圍

分割單個字符
與分割全部字符區域同理,在行和陣列中非 0 元素索引的范圍即是單個字符的區域,
l = ([i for i, e in enumerate(list1) if e != 0]) # 列和串列中不為0的索引
img_list = [] # 分割數字圖片存盤串列
temp = [] # 存盤某一個數字的所有行索引值
n = 0 # 數字圖片數量
for x in l:
temp.append(x)
if x+1 not in l: # 索引不連續的情況
if len(temp) != 1:
start_w = min(temp) # 索引最小值
end_w = max(temp) # 索引最大值
img_list.append(image[start_h:end_h, start_w:end_w]) # 對該索引包括數字切片
n += 1
temp = []
print(n) # 字符數

完整原始碼
import cv2
start_h, end_h, start_w, end_w = 0, 0, 0, 0 # 字符區域的高和寬起止
image = cv2.imread('F://001_1.jpg', 0) # 直接讀取為灰度圖
cv2.imshow('img_GRAY', image)
_, image = cv2.threshold(image, 50, 255, cv2.THRESH_BINARY) # 二值化
cv2.imshow('img_BINARY', image)
# 去噪點
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) # 簡單腐蝕膨脹核
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 簡單腐蝕膨脹核
image = cv2.erode(image, kernel=kernel1) # 腐蝕
image = cv2.dilate(image, kernel=kernel2) # 膨脹
cv2.imshow('img_denoise', image)
h, w = image.shape # 原圖的高和寬
# print(h, w)
list1 = [] # 列和
list2 = [] # 行和
for i in range(w):
list1.append(1 if image[:, i].sum() != 0 else 0) # 列求和,不為0置1
for i in range(h):
list2.append(1 if image[i, :].sum() != 0 else 0) # 行求和,不為0置1
# print(len(list1))
# print(len(list2))
# 裁剪字符區域
# 求行的范圍
flag = 0
for i, e in enumerate(list1):
if e != 0:
if flag == 0: # 第一個不為0的位置記錄
start_w = i
flag = 1
else: # 最后一個不為0的位置
end_w = i
# 求列的范圍
flag = 0
for i, e in enumerate(list2):
if e != 0:
if flag == 0: # 第一個不為0的位置記錄
start_h = i
flag = 1
else: # 最后一個不為0的位置
end_h = i
print(start_w, end_w) # 行索引范圍
print(start_h, end_h) # 列索引范圍
cv2.imshow('img_number', image[start_h:end_h, start_w:end_w])
l = ([i for i, e in enumerate(list1) if e != 0]) # 列和串列中不為0的索引
# print(l)
img_list = [] # 分割數字圖片存盤串列
temp = [] # 存盤某一個數字的所有行索引值
n = 0 # 數字圖片數量
for x in l:
temp.append(x)
if x+1 not in l: # 索引不連續的情況
if len(temp) != 1:
start_w = min(temp) # 索引最小值
end_w = max(temp) # 索引最大值
img_list.append(image[start_h:end_h, start_w:end_w]) # 對該索引包括數字切片
n += 1
# print(temp)
temp = []
print(n) # 字符數
for i in range(n): # 顯示保存字符
cv2.imshow('number'+str(i), img_list[i])
cv2.imwrite('F://demo'+str(i+1).zfill(2)+'.jpg', img_list[i])
cv2.waitKey(0)
結語
利用列向和行向相加的方法簡單分割字符的方法并不適用更加復雜的分割要求,另外演算法中也沒有考慮小數點分割問題,僅作為學習參考,歡迎有問題一起討論交流,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302434.html
標籤:AI
