前言
各位從事安防的童鞋可能都或多或少的涉及到人臉識(shí)別、車牌識(shí)別、視頻結(jié)構(gòu)化等應(yīng)用,但是這些應(yīng)用都是基于硬件實(shí)現(xiàn)的,比如本文將要提及的車牌識(shí)別算法一般都是硬件廠家集成到攝像機(jī)中算法,通過(guò)對(duì)接設(shè)備接口來(lái)實(shí)現(xiàn)車牌的識(shí)別,或者說(shuō)通過(guò)廠家的智能分析平臺(tái)集成的算法來(lái)實(shí)現(xiàn)人臉和車牌的識(shí)別。但是如果是通過(guò)攝像機(jī)或者廠家平臺(tái)實(shí)現(xiàn)人或車牌的算法存在如下問(wèn)題:
(1)購(gòu)買(mǎi)單攝像機(jī)成本較高且不能覆蓋所有位置,而不同位置都需要專業(yè)攝像機(jī),數(shù)量龐大
(2)不同廠家的專業(yè)攝像機(jī)接口不一,開(kāi)發(fā)接入成本較高
(3)廠家算法又不單獨(dú)對(duì)外提供,只能集成到廠家硬件或平臺(tái)中自己使用,無(wú)法做后期其他類型的自定制的應(yīng)用(如后期圖片識(shí)別與處理)
所以,針對(duì)以上問(wèn)題,我們?nèi)绾未蚱剖`,開(kāi)發(fā)出識(shí)別效率高、成本低的應(yīng)用就是接下來(lái)我要解決的問(wèn)題。
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
一、介紹
針對(duì)以上問(wèn)題,為了降低成本而又能獨(dú)立定制自己的應(yīng)用,最好的解決辦法是直接使用算法,我們有如下解決方案
(1)購(gòu)買(mǎi)百度、阿里等在線算法,按n元/次進(jìn)行收費(fèi)(缺點(diǎn)是必須在線聯(lián)網(wǎng)且持續(xù)性收費(fèi))
(2)使用第三方廠家提供的帶序列號(hào)的離線算法sdk
(3)使用開(kāi)源TensorFlow自訓(xùn)練ocr識(shí)別模型集成或其他開(kāi)源庫(kù)來(lái)實(shí)現(xiàn)
以上3中方案中
第一種:涉及到網(wǎng)絡(luò)問(wèn)題,針對(duì)很多政府項(xiàng)目或無(wú)互聯(lián)網(wǎng)的項(xiàng)目可能是行不通的;
第二種:使用離線sdk的方式是,算法是按照機(jī)器指紋或按加密狗進(jìn)行收費(fèi)的,資源不足或數(shù)據(jù)量較大的情況下集群多機(jī)部署可能購(gòu)買(mǎi)成本也較高。
第三種:也是我希望能長(zhǎng)期免費(fèi)且高效使用的一種方式,這種方式雖然后,但是難點(diǎn)在于要找到一個(gè)開(kāi)源的、穩(wěn)定的、識(shí)別率很高的、識(shí)別速度很快的就是一個(gè)很大的障礙!
二、paddle引入
在百度飛槳開(kāi)源之前,我找到很多的開(kāi)源庫(kù),包括TensorFlow自學(xué)習(xí)、easyLPR等等,不是編譯難度非常大、學(xué)習(xí)訓(xùn)練難度大就是識(shí)別速度慢、識(shí)別精度不高等問(wèn)題,每次調(diào)研每次都放棄了。
由于項(xiàng)目需要,這次又開(kāi)始了新的調(diào)研,不過(guò)網(wǎng)絡(luò)經(jīng)過(guò)幾年的技術(shù)革新和迭代,也有很多新的開(kāi)源項(xiàng)目出現(xiàn),包括口碑較好的EasyLPR的更新版本識(shí)別,我也同樣做了測(cè)試,得出一下結(jié)論
- 識(shí)別精度還是不高(一般在80-85%上下浮動(dòng)),部分車牌不識(shí)別
- 對(duì)中文識(shí)別支持較好,但是英文車牌就很差了,甚至不識(shí)別(港牌)
- 識(shí)別速度不快,有的可能要1秒或幾秒(圖片稍大情況)
基于以上情況,我有放棄了它!最終經(jīng)過(guò)多輪搜索和比對(duì)找到了飛槳!飛槳不是一個(gè)車牌識(shí)別庫(kù),而是一個(gè)適用于任何場(chǎng)景的的模型算法訓(xùn)練使用平臺(tái),其自身就集成了很多內(nèi)置算法模型,包括ocr識(shí)別、語(yǔ)音算法、語(yǔ)言翻譯等等,這些內(nèi)置的算法的精度已經(jīng)是很高很高了,可以和行業(yè)頂級(jí)算法媲美!
所以后續(xù)的車牌識(shí)別就是以百度飛槳平臺(tái)為基礎(chǔ),將飛槳的算法通過(guò)自寫(xiě)網(wǎng)絡(luò)api接口的方式暴露給自己的應(yīng)用程序進(jìn)行開(kāi)發(fā),實(shí)現(xiàn)應(yīng)用與算法的解耦!
三、車牌識(shí)別
針對(duì)車牌識(shí)別應(yīng)用,我的實(shí)現(xiàn)思路和流程如下
其實(shí)整個(gè)流程就是:
- 管理員動(dòng)態(tài)添加普通攝像機(jī)(非車牌識(shí)別專業(yè)攝像機(jī))到媒體接入服務(wù)
- 媒體接入服務(wù)通過(guò)rtsp協(xié)議從攝像機(jī)獲取到視頻流并通過(guò)抽幀采樣的方式抓取到圖片(采樣可以是25幀抽1幀或2幀甚至配置為n秒抓取一張圖)
- 媒體服務(wù)將采樣抽取的圖片幀通過(guò)python的restful api接口上傳到百度飛槳平臺(tái)平臺(tái)進(jìn)行識(shí)別,并返回結(jié)果
- 如果返回的識(shí)別結(jié)構(gòu)有車牌且與上一次車牌比較,如果是新車牌則自動(dòng)上傳推送到應(yīng)用平臺(tái)
- 應(yīng)用平臺(tái)可以通過(guò)自己的策略進(jìn)行定制的業(yè)務(wù)邏輯處理(如黑名單比對(duì)、計(jì)費(fèi)繳費(fèi)結(jié)算、遠(yuǎn)程抬杠<接入01網(wǎng)絡(luò)控制器即可>或者將識(shí)別結(jié)果通過(guò)websocket推送到web前端展示)
這里面的核心技術(shù)要點(diǎn)是
- 媒體接入服務(wù)接入rtsp通信視頻流并持續(xù)采樣抽幀
- 百度非飛槳平臺(tái)的搭建和部署,并通過(guò)編寫(xiě)python腳本對(duì)外提供OCR識(shí)別服務(wù)
- python腳本提供車牌的顏色識(shí)別服務(wù)并結(jié)合ocr識(shí)別服務(wù)形成車牌識(shí)別服務(wù)
以上的幾個(gè)要點(diǎn)這都已經(jīng)得到解決
-
媒體接入定時(shí)采樣存儲(chǔ)圖片到本地
-
百度OCR識(shí)別并提供網(wǎng)絡(luò)接口
訪問(wèn)python的測(cè)試demon,測(cè)試demon調(diào)用車牌識(shí)別接口
測(cè)試調(diào)用返回識(shí)別結(jié)果
測(cè)試其他的圖片
識(shí)別結(jié)果
港牌識(shí)別結(jié)果
英文港牌車牌識(shí)別更高!
- 顏色識(shí)別
飛槳只能識(shí)別到文字,但是需要集成車牌的識(shí)別,所以需要自己額外做顏色識(shí)別,這里可以使用opencv來(lái)處理
#圖片中目標(biāo)區(qū)域顏色的識(shí)別
def get_color(frame):
print('識(shí)別圖片選區(qū)顏色')
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
color_dict = getColorList()
#count = 0
maxsum = 0
color = None
#遍歷所有顏色匹配red|red2|orange|purple|blue|cyan|white|green
for d in color_dict:
#在后兩個(gè)參數(shù)范圍內(nèi)的值變成255
mask = cv2.inRange(hsv, color_dict[d][0], color_dict[d][1])
#在灰度圖片中,像素值大于127的都變成255,[1]表示調(diào)用圖像,也就是該函數(shù)第二個(gè)返回值
binary = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)[1]
# cv2.imshow("0",binary)
# cv2.waitKey(0)
# count+=1
#使用默認(rèn)內(nèi)核進(jìn)行膨脹操作,操作兩次,使縫隙變小,圖像更連續(xù)
binary = cv2.dilate(binary, None, iterations=2)
#獲取該函數(shù)倒數(shù)第二個(gè)返回值輪廓
cnts = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
#獲取該顏色所有輪廓圍成的面積的和
sum = 0
for c in cnts:
sum += cv2.contourArea(c)
if sum > maxsum:
maxsum = sum
color = d
if color == 'red2':
color = 'red'
elif color == 'orange':
color = 'yellow'
return color
各位網(wǎng)友如有合作可以聯(lián)系vx lixiang6153或秋秋941415509
四、完整代碼
這里貢獻(xiàn)出python端的完整代碼,實(shí)現(xiàn)了如下功能
- orc文字識(shí)別
- 車牌顏色識(shí)別
- 圖片上傳接口提供
具體代碼如下所示文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-479880.html
import os
import time
import cv2
import collections
from flask import Flask, request, send_from_directory
from werkzeug.utils import secure_filename
from paddleocr import PaddleOCR, draw_ocr
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
#監(jiān)聽(tīng)地址端口
host = '0.0.0.0'
port = 5600
#上傳文件大小限制
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
#關(guān)閉ascii編碼方式-返回中文
app.config['JSON_AS_ASCII'] = False
#允許上傳的文件類型和存儲(chǔ)位置
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg'])
UPLOAD_FOLDER = './files/'
#文件類型檢查函數(shù)
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
#創(chuàng)建ocr實(shí)例
ocr = PaddleOCR(lang="ch")
#獲取要識(shí)別的顏色列表
import numpy as np
import collections
# 將rgb圖像轉(zhuǎn)換為hsv圖像后,確定不同顏色的取值范圍
def getColorList():
dict = collections.defaultdict(list)
#black
lower_black = np.array([0, 0, 0])
upper_black = np.array([180, 255, 46])
color_list_black = []
color_list_black.append(lower_black)
color_list_black.append(upper_black)
dict['black'] = color_list_black
#white
lower_white = np.array([0, 0, 221])
upper_white = np.array([180, 30, 255])
color_list_white = []
color_list_white.append(lower_white)
color_list_white.append(upper_white)
dict['white'] = color_list_white
#yellow
lower_orange = np.array([11, 43, 46])
upper_orange = np.array([34, 255, 255])
color_list_orange = []
color_list_orange.append(lower_orange)
color_list_orange.append(upper_orange)
dict['yellow'] = color_list_orange
#green
lower_green = np.array([35, 43, 46])
upper_green = np.array([77, 255, 255])
color_list_green = []
color_list_green.append(lower_green)
color_list_green.append(upper_green)
dict['green'] = color_list_green
#blue
lower_blue = np.array([100, 43, 46])
upper_blue = np.array([124, 255, 255])
color_list_blue = []
color_list_blue.append(lower_blue)
color_list_blue.append(upper_blue)
dict['blue'] = color_list_blue
return dict
#圖片中目標(biāo)區(qū)域顏色的識(shí)別
def get_color(frame):
print('識(shí)別圖片選區(qū)顏色')
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
color_dict = getColorList()
#count = 0
maxsum = 0
color = None
#遍歷所有顏色匹配red|red2|orange|purple|blue|cyan|white|green
for d in color_dict:
#在后兩個(gè)參數(shù)范圍內(nèi)的值變成255
mask = cv2.inRange(hsv, color_dict[d][0], color_dict[d][1])
#在灰度圖片中,像素值大于127的都變成255,[1]表示調(diào)用圖像,也就是該函數(shù)第二個(gè)返回值
binary = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)[1]
# cv2.imshow("0",binary)
# cv2.waitKey(0)
# count+=1
#使用默認(rèn)內(nèi)核進(jìn)行膨脹操作,操作兩次,使縫隙變小,圖像更連續(xù)
binary = cv2.dilate(binary, None, iterations=2)
#獲取該函數(shù)倒數(shù)第二個(gè)返回值輪廓
cnts = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
#獲取該顏色所有輪廓圍成的面積的和
sum = 0
for c in cnts:
sum += cv2.contourArea(c)
if sum > maxsum:
maxsum = sum
color = d
if color == 'red2':
color = 'red'
elif color == 'orange':
color = 'yellow'
return color
#轉(zhuǎn)百分比,保留3位小數(shù)
def to_percent(pos, img):
return [round(pos[0] / img[0], 3),round(pos[0] / img[1], 3)]
#文件上次處理方法
@app.route('/detect', methods=['POST', 'GET'])
def do_upload():
#post上傳
if request.method == 'POST':
#獲取上傳文件
file = request.files['file']
#被允許上傳文件類型
if file and allowed_file(file.filename):
#開(kāi)始計(jì)算時(shí)間
old_time=time.time()
#獲取實(shí)際文件名
filename = secure_filename(file.filename)
#獲取保存圖片路徑
img_path = os.path.join(UPLOAD_FOLDER, filename)
#保存到設(shè)定目錄中
file.save(img_path)
#識(shí)別圖片
result = ocr.ocr(img_path)
if len(result) <= 0:
return {'error':-1, 'description':'圖片中未識(shí)別到文字'}
#opencv識(shí)別顏色
frame = cv2.imread(img_path)
size = [frame.shape[1],frame.shape[0]]
#獲取識(shí)別結(jié)果列表
list = []
for item in result[0]:
#識(shí)別選區(qū)顏色:[y:y+h, x:x+w]
rect = frame[int(item[0][0][1]):int(item[0][3][1]), int(item[0][0][0]):int(item[0][1][0])]
color = get_color(rect)
json = {
'text':item[1][0],
'color': color,
'precise': item[1][1],
'postion': {
'leftTop': to_percent(item[0][0],size),
'rightTop': to_percent(item[0][1],size),
'rightBottom': to_percent(item[0][2],size),
'leftBottom': to_percent(item[0][3],size)
}
}
list.append(json)
#計(jì)算耗時(shí)時(shí)間
take_time = time.time()-old_time;
#返回成功
return {
'error':0,
'time': take_time,
'width': size[0],
'height': size[1],
'value': list
}
return {'error':-1, 'description':'不支持的文件類型'}
#get請(qǐng)求獲取上傳網(wǎng)頁(yè)
return '''
<!doctype html>
<title>車牌識(shí)別</title>
<h1>車牌識(shí)別</h1>
<form action="" method=post enctype=multipart/form-data>
<p>
選擇車牌圖片:<input type=file name=file>
<input type=submit value=開(kāi)始識(shí)別>
</p>
</form>
'''
#下載指定文件
@app.route('/download/<filename>')
def uploaded_file(filename):
return send_from_directory(UPLOAD_FOLDER,filename)
#啟用應(yīng)用程序
if __name__ == '__main__':
app.run(host = host, port = port, debug = True)
以上流程都是可操作且成熟已實(shí)現(xiàn)的,如有問(wèn)題或需要合作,各位可與我取得聯(lián)系!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-479880.html
到了這里,關(guān)于普通攝像機(jī)之開(kāi)源實(shí)時(shí)車牌識(shí)別實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!