TensorFlow入門教程(26)車牌識別之文字檢測模型EAST程式碼實現(二)
#
#作者:韋訪
#部落格:https://blog.csdn.net/rookie_wei
#微信:1007895847
#新增微信的備註一下是CSDN的
#歡迎大家一起學習
#
1、概述
上一講,我們簡單是介紹了EAST的論文,有了理論依據以後,接下來我們來一步一步實現程式碼。為了照顧不做車牌檢測的網友,我們先來實現通用的自然場景下的文字檢測,再基於此實現車牌檢測。
環境配置:
作業系統:Ubuntu 64位
顯示卡:GTX 1080ti
Python:Python3.7
TensorFlow:2.3.0
2、ICDAR2017資料集
文字檢測有很多公開的資料集,我這裡選擇了ICDAR2017,因為這個資料集支援的語言種類比較多,而且資料集大小也不是幾百G的那種巨無霸。
官網連結:https://rrc.cvc.uab.es/?ch=8&com=downloads
百度網盤:https://pan.baidu.com/s/1S0a8cL743ZjvMzs6IZ_vrA 密碼: k6oj
資料集一共由11個壓縮包組成,包含了訓練集和驗證集的資料,我們將ch8_training開頭的壓縮包都解壓到ch8_training_images資料夾下,將ch8_validation開頭的壓縮包解壓到ch8_validation_images資料夾下,這樣比較方便我們操作。
上圖是ch8_training_images資料夾下的檔案總數,可以看到一共有14400個檔案,其中有7200個TXT文字檔案,和7200個jpg或png圖片檔案,他們通過檔名來一一對應。比如,圖片img_1.png對應的檔案是gt_img_1.txt。gt_img_1.txt檔案的內容如下圖所示,
上圖中,每一行代表一個文字框資訊,以逗號為分隔符,其中前8個欄位代表的是文字框的四個頂點的座標,分別為左上、右上、右下和左下。第9個欄位表示文字框內的文字屬於什麼語言。最後一個欄位表示文字框內的文字,”###”表示無法識別文字框內的文字內容,我們一般選擇忽略這種文字框。
3、資料增強
3.1、讀取文字框座標
首先,我們要根據圖片的檔名找到其對應的TXT文字檔案(TXT檔名只是比圖片檔名多了個“gt_”字首和字尾為“.txt”),然後再解析其中所有的文字框的座標資訊。由於”###”的表示不知道文字框內的文字內容,所以這種文字框我們選擇忽略,將它們在ignored_label列表中的值置為“True”。程式碼如下,
'''
獲取ICDAR資料集的圖片的檔名所對應的標籤文字檔案(包含文字框座標等資訊)
'''
def get_icdar_text_file(image_file):
# 文字檔名跟圖片檔名一樣,只是多了個gt_字首
txt_file = image_file.replace(os.path.basename(image_file).split('.')[1], 'txt')
txt_file_name = os.path.basename(txt_file)
txt_file = txt_file.replace(txt_file_name, 'gt_' + txt_file_name)
return txt_file
'''
通過txt匯入對應圖片的文字框座標等資訊
'''
def load_icdar_polys(image_file):
polys = []
ignored_label = []
# 找到對應的文字檔案
text_file = get_icdar_text_file(image_file)
if not os.path.exists(text_file):
return np.array(polys, dtype=np.float32)
with open(text_file, 'r', encoding="utf-8") as fd:
reader = csv.reader(fd)
for line in reader:
# strip BOM. \ufeff for python3, \xef\xbb\bf for python2
line = [i.strip('\ufeff').strip('\xef\xbb\xbf') for i in line]
# 獲取每行的文字框座標
x1, y1, x2, y2, x3, y3, x4, y4 = list(map(float, line[:8]))
poly = np.asarray([[x1, y1], [x2, y2], [x3, y3], [x4, y4]])
polys.append(poly)
# 每行的最後一個屬性,即文字框內的文字
label = line[-1]
# 如果文字是###,表示該文字框內的文字不清楚,我們忽略這種文字框
if label == '*' or label == "###":
ignored_label.append(True)
else:
ignored_label.append(False)
return np.array(polys, dtype=np.float32), np.array(ignored_label, dtype=np.bool)
3.2、隨機縮放圖片
隨機縮放是資料增強中常用的手段,我們隨機縮放圖片的寬和高,但是每次縮放的寬高比例不能相差太大,否則就失真了。程式碼如下,
'''
隨機縮放圖片和文字框座標
'''
def random_scale_image(image, polys):
random_scale = np.array([0.5, 0.75, 1., 1.25, 1.5])
rd_scale = np.random.choice(random_scale)
x_scale_variation = np.random.randint(-10, 10) / 100.
y_scale_variation = np.random.randint(-10, 10) / 100.
x_scale = rd_scale + x_scale_variation
y_scale = rd_scale + y_scale_variation
image = cv2.resize(image, dsize=None, fx=x_scale, fy=y_scale)
if len(polys) > 0:
polys[:, :, 0] *= x_scale
polys[:, :, 1] *= y_scale
return image, polys
3.3、隨機裁剪
接下來是隨機裁剪圖片了,分兩種情況,
第一種是裁剪後的圖片只有背景,沒有文字框,讓模型學會識別背景圖。
第二種是裁剪後的圖片至少包含一個文字框,讓模型學會識別文字框。需要注意的是,裁剪後,如果是帶文字框的,那麼,文字框的座標也要跟裁剪後的圖片的座標對應得上,文字框是否是應該忽略的標籤資訊也不能丟。
先來看整體的程式碼,再具體看應該怎麼裁剪,整體程式碼如下,
'''
隨機擷取圖片中的一個區域
'''
def random_crop_area(FLAGS, image, polys, ignored_labels):
# DEBUG = True
h, w, _ = image.shape
# 計算最小擷取寬度和高度
min_crop_w = np.round(FLAGS.min_crop_side_ratio * w).astype(np.int32)
min_crop_h = np.round(FLAGS.min_crop_side_ratio * h).astype(np.int32)
# 如果該圖片沒有文字框資訊,則直接隨機擷取
if len(polys) < 1:
return random_crop_backgroup_area(FLAGS, image, min_crop_w, min_crop_h)
rectangle_polys = []
crop_image = []
crop_polys = []
crop_ignored_labels = []
# 將文字框變換成矩形的形式
for poly in polys:
# round
poly = np.round(poly, decimals=0).astype(np.int32)
min_x = np.min(poly[:, 0])
max_x = np.max(poly[:, 0])
min_y = np.min(poly[:, 1])
max_y = np.max(poly[:, 1])
rectangle_polys.append([[min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y]])
rectangle_polys = np.asarray(rectangle_polys)
# 隨機獲取背景截圖或帶文字框的截圖
if np.random.rand() < FLAGS.background_ratio:
crop_image, crop_polys, crop_ignored_labels = random_crop_backgroup_area_with_polys(image, rectangle_polys, min_crop_w, min_crop_h)
# print("background")
else:
crop_image, crop_polys, crop_ignored_labels = random_crop_text_area(image, polys, rectangle_polys, ignored_labels, min_crop_w, min_crop_h)
# print("text")
# 如果文字框座標長度和截圖的長度都為0,則表示擷取失敗,則直接返回原圖和原座標
if len(crop_image) < 1 and len(crop_polys) < 1:
crop_image = image
crop_polys = polys
crop_ignored_labels = ignored_labels
if DEBUG:
for poly in crop_polys:
crop_image = draw_line(crop_image, poly)
if len(crop_image) > 0:
crop_image = cv2.resize(crop_image, (512, 512))
image = cv2.resize(image, (800, 800))
cv2.imshow("crop_image", crop_image)
cv2.imshow("image", image)
cv2.waitKey(0)
# show(image)
return crop_image, crop_polys, crop_ignored_labels
上面程式碼中,如果送進來的圖片沒有文字框資訊,則隨機擷取,然後返回。如果送進來的圖片有文字框,那麼,根據設定的FLAGS.background_ratio隨機選擇這次是裁剪背景圖還是裁剪包含文字框的圖,然後返回裁剪後的圖片資訊、文字框座標和忽略標籤即可。
3.3.1、隨機裁剪背景圖
先來看看怎麼隨機裁剪背景圖。函式名為random_crop_backgroup_area_with_polys,程式碼如下,
'''
隨機擷取沒有文字的背景區域
'''
def random_crop_backgroup_area_with_polys(image, rectangle_polys, min_crop_w, min_crop_h):
# DEBUG = True
crop_image = []
crop_polys = []
crop_ignored_labels = []
h, w, _ = image.shape
# 隨機生成要擷取的圖片的x軸的起始座標
crop_x = np.random.randint(0, w - min_crop_w - 1)
if DEBUG:
cv2.circle(image, (crop_x, 0), 2, (0,255,0), 4)
cv2.imshow("image", image)
cv2.waitKey(0)
# 隨機生成要擷取的圖片的x軸的x軸寬度
crop_w = np.random.randint(min_crop_w, w - crop_x - 1)
if DEBUG:
cv2.line(image, (crop_x, 0), (crop_x + crop_w, 0), (255,0,0), 4)
cv2.imshow("image", image)
cv2.waitKey(0)
# print("crop_x:", crop_x, " crop_w:", crop_w)
# print("len polys:", len(polys))
# 找到x軸跟點crop_x到crop_x+crop_w有交集的文字框
relevant_rectangle_polys = []
for poly in rectangle_polys:
if (crop_x >= poly[0][0] and crop_x <= poly[1][0]) or (crop_x + crop_w >= poly[0][0] and crop_x + crop_w <= poly[1][0]) or (crop_x <= poly[0][0] and crop_x + crop_w >= poly[1][0]):
relevant_rectangle_polys.append(poly)
# print("len relevant_rectangle_polys:", len(relevant_rectangle_polys))
# 將與擷取圖相關的文字框的y軸標記,被標記的區域是不能選的
h_array = np.zeros((h), dtype=np.int32)
for poly in relevant_rectangle_polys:
# print(poly)
min_h = np.min(poly[:, 1])
max_h = np.max(poly[:, 1])
# print("min_h:", min_h, " max_h:", max_h)
h_start = np.where(min_h - min_crop_h > 0, min_h - min_crop_h, 0)
h_end = np.where(max_h + min_crop_h < h, max_h + min_crop_h, h)
# print("h_start:", h_start, " h_end:", h_end)
h_array[h_start : h_end] = 1
# print("h_array:", h_array)
# 將y軸中自底向上的min_crop_h長度的區域標記
h_array[h-min_crop_h : ] = 1
# 算出未被標記的y軸座標,要擷取的圖片的y軸起始座標可以在這個區域隨機生成
h_axis = np.where(h_array == 0)[0]
# print("h_axis:", h_axis)
if len(h_axis) > 0:
# print("h_axis:", h_axis)
# 隨機獲取擷取圖的y軸起始座標
crop_y = np.random.choice(h_axis, size=1)[0]
if DEBUG:
cv2.circle(image, (0, crop_y), 2, (0,255,0), 4)
cv2.imshow("image", image)
cv2.waitKey(0)
# print("h_axis:", h_axis, " crop_y:", crop_y)
# 找到h_axis中,crop_y往上的第一個不連續的點的座標,用於限定隨機生成的擷取高度
len_h_axis = len(h_axis)
# print("h_axis.index(crop_y):", np.argwhere(h_axis == crop_y), " crop_y:", crop_y)
discontinuity = 0
for i in range(np.argwhere(h_axis == crop_y)[0][0], len_h_axis, 1):
# print("i:", i, " h_axis[i]:", h_axis[i], " h_axis[i]+1:", h_axis[i+1] - 1)
if i < len_h_axis - 1 and h_axis[i] != h_axis[i+1] - 1:
discontinuity = h_axis[i]
break
if i == len_h_axis - 1:
discontinuity = h_axis[i]
# print("crop_y:", crop_y, "discontinuity:", discontinuity)
if discontinuity != 0:
# 隨機生成高度
crop_h = np.random.randint(min_crop_h, discontinuity + min_crop_h - crop_y + 1)
if DEBUG:
cv2.line(image, (0, crop_y), (0, crop_y + crop_h), (255,0,0), 4)
cv2.imshow("image", image)
print("crop_x:", crop_x, " crop_w:", crop_w)
print("crop_y:", crop_y, " crop_h:", crop_h)
image = cv2.line(image, (crop_x, crop_y), (crop_x + crop_w, crop_y), (255,0,0), thickness=2)
image = cv2.line(image, (crop_x + crop_w, crop_y), (crop_x + crop_w, crop_y + crop_h), (255,0,0), thickness=2)
image = cv2.line(image, (crop_x + crop_w, crop_y + crop_h), (crop_x, crop_y + crop_h), (255,0,0), thickness=2)
image = cv2.line(image, (crop_x, crop_y + crop_h), (crop_x, crop_y), (255,0,0), thickness=2)
cv2.waitKey(0)
# 擷取影像
crop_image = image[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w, :]
return crop_image, crop_polys, crop_ignored_labels
這部分程式碼可能有點難理解,我看了其他開原始碼,都是採用“碰運氣式”的裁剪,也就是說,先把所有文字框的x和y軸對映出來,這部分割槽域都不能選,然後再隨機擷取其他區域的,如果擷取的區域包含了文字框,就再隨機擷取,直到不包含文字框為止。這種方法比較簡單,但是效率比較低。我上面裁剪程式碼的思路是,
- 先將所有的文字框變成與x軸平行的矩形的形式,即找到四個頂點中的最大和最小的x座標和y座標組成的矩形。
- 然後隨機生成要擷取的圖片的x軸的起始座標,再隨機生成要擷取圖片的寬度,如下圖示。
- 找到所有x軸與步驟“2”中的直線有交集的文字框,稱為相關文字框。
- 將所有相關文字框的y軸進行對映,對h-min_crop_h區域也進行對映。
- 在步驟“4”中,在未被對映的區域中隨機選擇擷取圖的y軸的起始座標,如下圖所示。
- 從步驟”5”中的y軸起始座標往上找(這裡的往上對應於圖片是往下,因為計算機中,原點的座標一般都是左上角),在未被對映列表中找到第一個不連續點的座標,可以在這中間隨機生成擷取圖片的高度,如下圖所示。
- 最後,根據x軸、y軸的起點座標,以及寬和高,就得到要擷取的矩形框座標,如下圖所示。
3.3.2、隨機裁剪包含文字框的截圖
接下來看看隨機裁剪帶文字框的截圖,程式碼如下,
'''
隨機擷取包含文字框的區域
'''
def random_crop_text_area(image, polys, rectangle_polys, ignored_labels, min_crop_w, min_crop_h):
# DEBUG = True
crop_image = []
crop_polys = []
crop_ignored_labels = []
h, w, _ = image.shape
# print("rectangle_polys:", rectangle_polys)
# 標記x軸和y軸中所有文字框對映的區域,該區域不能為起始座標
w_array = np.zeros((w), dtype=np.int32)
h_array = np.zeros((h), dtype=np.int32)
padding = 1
for poly in rectangle_polys:
# 求該文字座標中的x軸的最大和最小點
minx = np.where(np.min(poly[:, 0]) - padding > 0, np.min(poly[:, 0]) - padding, 0)
maxx = np.where(np.max(poly[:, 0]) + padding > w, w, np.max(poly[:, 0]) + padding)
# 將w_array中對應的文字座標x軸往外擴充套件padding設定為1
w_array[minx:maxx] = 1
# 求該文字座標中的y軸的最大和最小點
miny = np.where(np.min(poly[:, 1]) - padding > 0, np.min(poly[:, 1]) - padding, 0)
maxy = np.where(np.max(poly[:, 1]) + padding > h, h, np.max(poly[:, 1]) + padding)
# 將h_array中對應的文字座標y軸往外擴充套件padding設定為1
h_array[miny:maxy] = 1
# 找到x軸中,最右的文字框左上角的x座標,這個點往後的都標記為1,這些區域不能作為擷取點的左上角頂點
txt_rect_max_x = np.max(rectangle_polys[:,:,0])
w_array[txt_rect_max_x:] = 1
# print("txt_rect_max_x:", w_array)
# 找到y軸中,最底部的文字框的左上角的y座標,這個點往下的都標記為1,這些區域不能作為擷取點的左上角頂點
txt_rect_max_y = np.max(rectangle_polys[:,:,1])
h_array[txt_rect_max_y:] = 1
# print("txt_rect_max_y:", h_array)
# 求未被標記的x軸和y軸座標
w_axis = np.where(w_array == 0)[0]
h_axis = np.where(h_array == 0)[0]
# 如果都被標記了,就沒法裁剪了,直接返回空
if len(w_axis) < 1 or len(h_axis) < 1:
return crop_image, crop_polys, crop_ignored_labels
# 隨機生成擷取圖左上角的座標x和y
crop_x = np.random.choice(w_axis, size=1)[0]
crop_y = np.random.choice(h_axis, size=1)[0]
if DEBUG:
cv2.circle(image, (crop_x, crop_y), 2, (0,255,0), 4)
cv2.imshow("image", image)
cv2.waitKey(0)
# 將座標x和y往右的所有文字框找出來,這些文字框為相關框
relevant_rectangle_polys = []
for poly in rectangle_polys:
if crop_x <= poly[0][0] and crop_y <= poly[0][1]:
relevant_rectangle_polys.append(poly)
relevant_rectangle_polys = np.asarray(relevant_rectangle_polys)
# 如果沒有包含相關框,表示沒裁剪到文字框,直接返回空
if len(relevant_rectangle_polys) < 1:
return crop_image, crop_polys, crop_ignored_labels
# print("relevant_rectangle_polys:", relevant_rectangle_polys)
# 將相關框的x軸和y軸進行標記
w_array_relevant = np.zeros((w), dtype=np.int32)
h_array_relevant = np.zeros((h), dtype=np.int32)
for poly in relevant_rectangle_polys:
# 求該文字座標中的x軸的最大和最小點
minx = np.where(np.min(poly[:, 0]) - padding > 0, np.min(poly[:, 0]) - padding, 0)
maxx = np.where(np.max(poly[:, 0]) + padding > w, w, np.max(poly[:, 0]) + padding)
# 將w_array_relevant中對應的文字座標x軸往外擴充套件padding設定為1
w_array_relevant[minx:maxx] = 1
# 求該文字座標中的y軸的最大和最小點
miny = np.where(np.min(poly[:, 1]) - padding > 0, np.min(poly[:, 1]) - padding, 0)
maxy = np.where(np.max(poly[:, 1]) + padding > h, h, np.max(poly[:, 1]) + padding)
# 將h_array_relevant中對應的文字座標y軸往外擴充套件padding設定為1
h_array_relevant[miny:maxy] = 1
# 找到x軸中,最左的文字框左上角的x座標,這個點往前的都標記為1,如果右下角頂點在這個區域就框不到文字框了
txt_rect_min_x = np.max(relevant_rectangle_polys[:,:,0])
w_array_relevant[:txt_rect_min_x] = 1
# print("w_array_relevant:", w_array_relevant)
# 找到y軸中,最底部的文字框的左上角的y座標,這個點往上的都標記為1,如果右下角頂點在這個區域就框不到文字框了
txt_rect_min_y = np.max(relevant_rectangle_polys[:,:,1])
h_array_relevant[:txt_rect_min_y] = 1
# print("h_array_relevant:", h_array_relevant)
# x軸從crop_x到crop_x+min_crop_w都標記為1,否則擷取的寬度達不到要求
w_array_relevant[crop_x : crop_x+min_crop_w] = 1
# y軸從crop_y到crop_y+min_crop_y都標記為1,否則擷取的高度達不到要求
h_array_relevant[crop_y : crop_y+min_crop_h] = 1
# 求未被標記的x軸和y軸座標
w_axis_relevant = np.where(w_array_relevant == 0)[0]
h_axis_relevant = np.where(h_array_relevant == 0)[0]
# print("w_axis:", w_axis_relevant)
# print("h_axis:", h_axis_relevant)
# 如果都被標記了,表示沒法裁剪,直接返回空
if len(w_axis_relevant) < 1 or len(h_axis_relevant) < 1:
return crop_image, crop_polys, crop_ignored_labels
# 隨機選擇擷取圖的寬高
crop_w = np.random.choice(w_axis_relevant, size=1)[0]
crop_h = np.random.choice(h_axis_relevant, size=1)[0]
crop_w -= crop_x
crop_h -= crop_y
if DEBUG:
image = cv2.line(image, (crop_x, crop_y), (crop_x + crop_w, crop_y), (255,0,0), thickness=2)
image = cv2.line(image, (crop_x + crop_w, crop_y), (crop_x + crop_w, crop_y + crop_h), (255,0,0), thickness=2)
image = cv2.line(image, (crop_x + crop_w, crop_y + crop_h), (crop_x, crop_y + crop_h), (255,0,0), thickness=2)
image = cv2.line(image, (crop_x, crop_y + crop_h), (crop_x, crop_y), (255,0,0), thickness=2)
cv2.imshow("image", image)
cv2.waitKey(0)
# 擷取影像
crop_image = image[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w, :]
# 找到原文字框中的相關框
for poly, label in zip(polys, ignored_labels):
if (crop_x <= poly[0][0] and crop_y <= poly[0][1] and (crop_x + crop_w) >= poly[0][0] and (crop_y + crop_h) >= poly[0][1]):
crop_polys.append(poly)
crop_ignored_labels.append(label)
crop_polys = np.asarray(crop_polys)
crop_ignored_labels = np.asarray(crop_ignored_labels)
# print("crop_x:", crop_x, "crop_y:", crop_y)
# print("crop_polys:", crop_polys)
crop_polys[:,:,0] -= crop_x
crop_polys[:,:,1] -= crop_y
# print("crop_polys after:", crop_polys)
return crop_image, crop_polys, crop_ignored_labels
上面程式碼的思路是:
- 先對映所有文字框的x軸和y軸,這些區域不能被選為起始座標。
- 找到最右邊和最下邊的文字框,這個文字框往右和往下的區域都標記為不能選為起始座標的區域。
- 在未被標記的區域中隨機生成x軸和y軸的起始座標,如下圖所示。
- 找到起始座標往右和往下的所有文字框,稱為相關文字框。
- 將所有相關文字框的x和y軸進行對映,並且將最左文字框往左的區域和最上的文字框往上的區域都進行對映,這個區域不能選為擷取圖的右下角頂點。
- 然後在未被對映的區域中隨機生成擷取圖的寬和高(即擷取框右下角頂點座標),如下圖所示。
- 最後重新計算擷取圖中的文字框相對於擷取圖的座標,並返回。
3.4、填充
上面進行隨機裁剪後,得到的裁剪圖大小不一,如果直接進行縮放,那麼就會導致嚴重的失真,所以先對裁剪後的影像進行填充。填充圖的大小取裁剪圖的寬、高和我們預設的模型輸入大小中最大的一個,程式碼如下,
'''
為了不讓原圖過度變形,對擷取後的圖片進行填充
'''
def pad_image(image, polys, input_size):
# DEBUG = True
h, w, _ = image.shape
max_h_w_i = np.max([h, w, input_size])
img_padded = np.zeros((max_h_w_i, max_h_w_i, 3), dtype=np.uint8)
shift_h = np.random.randint(max_h_w_i - h + 1)
shift_w = np.random.randint(max_h_w_i - w + 1)
img_padded[shift_h:h+shift_h, shift_w:w+shift_w, :] = image.copy()
if DEBUG:
cv2.imshow("pad", img_padded)
cv2.waitKey(0)
if len(polys) > 0:
polys[:, :, 0] += shift_w
polys[:, :, 1] += shift_h
return img_padded, polys
執行結果,
3.5、縮放成固定大小圖片
最後對圖片進行縮放,縮放至我們預設的模型輸入大小。雖然模型並不會要求輸入影像的寬高,但是在訓練中,我們還是會指定輸入影像的寬高的,這樣才能進行批量訓練。程式碼如下,
'''
將圖片縮放成固定大小
'''
def resize(image, polys, input_size):
h, w, _ = image.shape
image = cv2.resize(image, dsize=(input_size, input_size))
resize_ratio_x = input_size/float(w)
resize_ratio_y = input_size/float(h)
if len(polys) > 0:
polys[:, :, 0] *= resize_ratio_x
polys[:, :, 1] *= resize_ratio_y
return image, polys
相關文章
- 車牌識別系統、車牌識別整合、車牌識別介面
- GitHub車牌檢測識別專案調研Github
- 文字識別(五)--自然場景文字檢測技術綜述(CTPN, SegLink, EAST)AST
- 文字檢測模型EAST應用詳解 ckpt pb的tf載入,opencv載入模型ASTOpenCV
- 移動端的車牌識別如何實現
- python opencv識別藍牌車牌號 之 取出車牌號 (1/3)PythonOpenCV
- 智慧城市車牌識別
- 車牌識別系統
- 車牌識別資料
- Crystal 實現文字識別程式
- 車牌識別助力“智慧停車”
- IOS人臉識別開發入門教程--人臉檢測篇iOS
- 智慧車牌識別相機
- 車牌識別字元模板庫字元
- 車牌識別一體機二次開發
- 渣土車識別檢測系統
- 【TensorFlow篇】--Tensorflow框架實現SoftMax模型識別手寫數字集框架模型
- 文字檢測與識別資源
- js實現的檢測文字框輸入是否是數字的程式碼JS
- PaddlePaddle車牌識別實戰和心得
- 課程設計-車牌檢測
- 車牌識別服務-JAVA+ONNX版本,支援全型別的車牌Java型別
- 基於matlab的車牌識別(含子程式)Matlab
- 利用 D 程式語言實現文字識別程式
- 車牌識別相機助力智慧巡檢車路側停車移動計費
- 車牌識別相機及簡介
- 工地渣土車清洗識別檢測系統
- Node.js車牌識別、文件識別、OCR API-自動化錄入資訊Node.jsAPI
- [深度學習]人臉檢測-Tensorflow2.x keras程式碼實現深度學習Keras
- 使用 Elixir 實現簡單的文字識別程式
- 使用 Tcl 實現簡單的文字識別程式
- 移動端車牌識別的應用
- 目標檢測入門系列手冊二:RCNN訓練教程CNN
- 文字識別——檢測部分 CTPN論文翻譯
- 2021車牌識別相機技術發展現狀
- 純web端實現二維碼識別Web
- 使用 Fantom 實現簡單的文字識別程式
- 人臉檢測識別,人臉檢測,人臉識別,離線檢測,C#原始碼C#原始碼