【python+yolov5+paddleocr 监测+OCR识别非标车牌】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

非标车牌识别主要用于识别和区分不符合安全标准的车牌,本次案例应用于厂区内部车辆限速控制,便于执法部门快速定位违规车辆,减少交通违法行为。

一、PaddleOCR是什么?

PaddleOCR,全称Paddle Optical Character Recognition,是百度基于其深度学习框架PaddlePaddle开发的一款功能强大的开源OCR工具。它利用深度学习技术,实现了高精度的文字检测、文字识别及版面分析等功能,广泛应用于文档数字化、车牌识别、验证码识别等多个领域。本文将简明扼要地介绍PaddleOCR的核心技术、应用场景及实践方法。

二、yolov5在项目中的作用?

通过yolov5实现自定义非标车牌的模型训练及检测车牌位置,并剪切车牌,用于OCR识别。

三、使用步骤

1.引入库

代码如下:

# -*- coding: utf-8 -*-

import asyncio
import logging
import os
import threading
import time
from collections import deque
from datetime import date
from requests_toolbelt import MultipartEncoder

import cv2
import numpy as np
import torch
from paddleocr import TextRecognition

import cgi
import requests
import re

from socketserver import ThreadingMixIn

from http.server import HTTPServer, BaseHTTPRequestHandler

2.OCR识别车牌

代码如下:

# 请求去重缓存
processed_requests = deque(maxlen=1000)

# 配置日志
logging.basicConfig(
    level=logging.DEBUG,  # 设置日志级别
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    filename='logs/app.log',  # 日志文件名
    filemode='a'  # 追加模式
)

def cv_imread(file_path):
    # 使用二进制方式读取文件并解码
    img_array = np.fromfile(file_path, np.uint8)
    img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
    return img[..., ::-1]  # BGR to RGB





class FileUploadHandler(BaseHTTPRequestHandler):
    # 用于存储最近请求的时间戳
    recent_requests = {}
    recent_requests_time = {}
    # 设置请求间隔时间(例如,1秒)
    request_interval = 2.0

    def do_POST(self):
        # 设置返回类型为 HTML
        self.send_response(200)
        self.send_header('Content-type', 'application/json;charset=utf-8')
        self.end_headers()


        bodystr = self.path
        if bodystr.find("uploadImg") > -1:
            request_key = f"{self.client_address[0]}:{self.path}"
            # {self.client_address[1]}:
            current_time = time.time()
            print(request_key)
            if request_key in self.recent_requests and (current_time - self.recent_requests[request_key] < self.request_interval):
                logging.warning("重复请求: " + request_key)
                self.send_response(200)
                self.send_header('Content-type', 'application/json;charset=utf-8')
                self.end_headers()
                self.wfile.write(bytes('{"code":201,"msg":"request already processed"}', "utf8"))
                return
            # 更新最近请求的时间戳
            self.recent_requests[request_key] = current_time

            # 解析表单数据
            form = cgi.FieldStorage(fp=self.rfile, headers=self.headers,
                                    environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type']})



            self.send_response(200)
            self.send_header('Content-type', 'application/json;charset=utf-8')
            self.end_headers()
            # 获取当前日期
            today = date.today()
            # 格式化为年--日
            date_str = today.strftime('%Y%m%d')
            fileName = ''
            # 获取图片
            for field in form.keys():
                item = form[field]
                if item.filename:  # 文件上传字段
                    imgfile = f'carimg/' + item.filename
                    fileName = item.filename
                    with open(f'carimg/{item.filename}', 'wb') as f:
                        f.write(item.file.read())
                    # print(f'Saved file: {item.filename}')
                    logging.info(f'Saved file:{item.filename}')

            if os.path.exists(imgfile):
                equipment_No = form.getvalue('equipment_No')  # 设备唯一编号
                shotTime = form.getvalue('shotTime')  # 抓拍时间 241031153119
                carNumber = form.getvalue('carNumber')  # 车牌号 黑A 123456
                plateColor = form.getvalue('plateColor')  # 车牌颜色
                vehicleColor = form.getvalue('vehicleColor')  # 车辆颜色
                vehicleType = form.getvalue('vehicleType')  # 车辆类型
                speed = form.getvalue('speed')  # 速度
                alarm = form.getvalue('alarm')  # 报警阈值
                add_osd = form.getvalue('add_osd')  # 默认”0”
                type = form.getvalue('type')  # 默认”car”,报警事件为”alarm”
                AlarmEventType = form.getvalue('AlarmEventType')  # 仅报警事件上传此字段
                dev_address = form.getvalue('dev_address')  # 速设备点位地址信息
                alarm_type = form.getvalue('alarm_type')  # 报警类型 0 未超速 1 超速
                ongitude = form.getvalue('ongitude')  # 经度
                latitude = form.getvalue('latitude')  # 纬度

                message = "{\"code\":200,\"result\":{\"url\":\"/image/car/"+date_str+"/"+equipment_No+"_"+fileName+"\"},\"msg\":\"ok\"}"
                logging.info("retrun msg : " + message)
                self.wfile.write(bytes(message, "utf8"))

                # 异步处理
                # asyncio.run(process_image_async(fileName,imgfile,equipment_No,shotTime,carNumber,plateColor,vehicleColor,vehicleType,speed,alarm,add_osd,type,AlarmEventType,dev_address,alarm_type,ongitude,latitude))

            # thread = threading.Thread(target=self.process_image_async, args=(form,))
            # thread.start()
            # 创建并启动一个新线程来运行异步任务
            thread = threading.Thread(target=thread_function,args=(fileName,imgfile,equipment_No,shotTime,carNumber,plateColor,vehicleColor,vehicleType,speed,alarm,add_osd,type,AlarmEventType,dev_address,alarm_type,ongitude,latitude))
            thread.start()
            # thread.join()  # 等待线程完成其工作
        else:
            self.send_response(200)
            self.send_header('Content-type', 'application/json;charset=utf-8')
            self.end_headers()
            message = "{\"code\":201,\"result\":{\"fuck\":},\"msg\":\"ok\"}"
            self.wfile.write(bytes(message, "utf8"))

    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'application/json;charset=utf-8')
        self.end_headers()
        message = b"<html><body><form method='post' action='' enctype='multipart/form-data'>Upload a file: <input type='file' name='file'><br><input type='submit' value='Upload'></form></body></html>"
        self.wfile.write(bytes(message, "utf8"))


async def process_image_async(fileName,imgfile,equipment_No,shotTime,carNumber,plateColor,vehicleColor,vehicleType,speed,alarm,add_osd,type,AlarmEventType,dev_address,alarm_type,ongitude,latitude):
    form_data = {}
    files = {}
    # 获取图片
    logging.warning("接收数据:" + imgfile + "--" + str(equipment_No) + "--" + str(shotTime))
    # img = cv2.imread(imgfile)[..., ::-1]
    img = cv_imread(imgfile)
    results = model(img)  # batch of images
    results_ = results.pandas().xyxy[0].to_numpy()
    # results.print()
    wuliuflag = 0
    left_up_y = -1
    right_down_y = -1
    left_up_x = -1
    right_down_x = -1
    # 获取车牌区域并识别
    for box in results_:
        l, t, r, b = box[:4].astype('int')  # 获取边界框坐标
        confidence = str(round(box[4], 1))  # 获取置信度
        cls_name = box[6]

        if float(confidence) > 0.4 and cls_name == "carposi":
            left_up_y = t
            right_down_y = b
            left_up_x = l
            right_down_x = r

    if right_down_x>0 and left_up_x > 0:

        plate_region = img[int(left_up_y):int(right_down_y), int(left_up_x):int(right_down_x)]

        # 1.灰度图
        gray = cv2.cvtColor(plate_region, cv2.COLOR_BGR2GRAY)

        # 2. 去噪和增强
        blurred = cv2.GaussianBlur(gray, (3, 3), 0)
        equalized = cv2.equalizeHist(blurred)
        # cv2.imwrite("D:\\soft\\car_regnize\\carimg\\blurred.jpg", equalized)

        # 3. 二值化 参数: 图像, 阈值, 最大值, 二值化类型
        # _, binary = cv2.threshold(equalized, 60, 255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

        # 4. 形态学操作
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1))
        cleaned = cv2.morphologyEx(equalized, cv2.MORPH_CLOSE, kernel)

        name = int(time.time() * 1000)

        cut_img = "D:\\soft\\car_regnize\\carimg\\cut\\" + str(name) + ".jpg"
        # cut_img = "D:\\yolov5-master\\carimg\\cut\\" + str(name) + ".jpg"



        cv2.imwrite(cut_img, cleaned)

        # PaddleOCR OCR 推理
        paddleresult = ocr_model.predict(input=cut_img)

        ocr_smart_text = ""
        # 可视化结果并保存 json 结果
        for res in paddleresult:
            # res.print()
            # res.save_to_img("output")
            # res.save_to_json("output")
            rec_text = res['rec_text']
            rec_score = res['rec_score']
            cleaned_text = re.sub(r'[^A-Za-z0-9]', '', rec_text)
            print(rec_text + " length: " + str(len(rec_text)) + " " + str(rec_score) + "  " + cleaned_text)
            if rec_score > 0.5:
                ocr_smart_text = cleaned_text
                if len(cleaned_text) >= 6 :
                    wuliuflag = 0
                    print('正常车牌不处理' + cut_img)
                elif len(cleaned_text) == 3:
                    wuliuflag = 1
                    print('物流车辆' + cut_img)
                else:
                    wuliuflag = 2
                    ocr_smart_text = ""

        # 识别到非标车牌
        if wuliuflag == 1:
            ocr_smart_text = "物流 " + ocr_smart_text[-3:]
            print(ocr_smart_text)
            # 准备文件数据
            form_data = {
                "equipment_No": equipment_No,
                "shotTime": shotTime,
                "carNumber": ocr_smart_text,
                "plateColor": plateColor,
                "vehicleColor": vehicleColor,
                "vehicleType": vehicleType,
                "speed": speed,
                "alarm": alarm,
                "add_osd": add_osd,
                "type": type,
                "AlarmEventType": AlarmEventType,
                "dev_address": dev_address,
                "alarm_type": alarm_type,
                "ongitude": ongitude,
                "latitude": latitude
            }
            files = {
                'file': (fileName, open(imgfile, 'rb'), 'image/jpeg')
            }
            logging.warning("识别wl发送数据:" + imgfile + str(equipment_No) + "--" + str(shotTime))
        elif wuliuflag == 0:
            # 准备文件数据
            form_data = {
                "equipment_No": equipment_No,
                "shotTime": shotTime,
                "carNumber": carNumber,
                "plateColor": plateColor,
                "vehicleColor": vehicleColor,
                "vehicleType": vehicleType,
                "speed": speed,
                "alarm": alarm,
                "add_osd": add_osd,
                "type": type,
                "AlarmEventType": AlarmEventType,
                "dev_address": dev_address,
                "alarm_type": alarm_type,
                "ongitude": ongitude,
                "latitude": latitude
            }
            files = {
                'file': (fileName, open(imgfile, 'rb'), 'image/jpeg')
            }
            logging.warning("识别正常发送数据:" + imgfile + str(equipment_No) + "--" + str(shotTime))
        else:
            logging.warning("识别cw不发送数据:" + imgfile + str(equipment_No) + "--" + str(shotTime))


    else:
        print("标准车牌不需处理直接返给平台")
        # 准备文件数据
        form_data = {
            # 'file': (imgfile, file),
            "equipment_No": equipment_No,
            "shotTime": shotTime,
            "carNumber": carNumber,
            "plateColor": plateColor,
            "vehicleColor": vehicleColor,
            "vehicleType": vehicleType,
            "speed": speed,
            "alarm": alarm,
            "add_osd": add_osd,
            "type": type,
            "AlarmEventType": AlarmEventType,
            "dev_address": dev_address,
            "alarm_type": alarm_type,
            "ongitude": ongitude,
            "latitude": latitude
        }

        files={
            'file':(fileName, open(imgfile, 'rb'), 'image/jpeg')
        }

    try:

        headers = {
            'Content-type': "application/form-data",  # 如果需要认证的话
        }
        # 发送POST请求
        # data=form_data
        # response = requests.post(post_url, files=files, verify=False, timeout=3)
        response = requests.request("POST", post_url, files=files,data=form_data,verify=False, timeout=3)
        print(response.text)
    except requests.exceptions.HTTPError as errh:
        print(f'Http Error: {errh}')
    except requests.exceptions.ConnectionError as errc:
        print(f'Error Connecting: {errc}')
    except requests.exceptions.Timeout as errt:
        print(f'Timeout Error: {errt}')
    except requests.exceptions.RequestException as err:
        print(f'OOps: Something Else {err}')

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""
    pass

def thread_function(fileName,imgfile,equipment_No,shotTime,carNumber,plateColor,vehicleColor,vehicleType,speed,alarm,add_osd,type,AlarmEventType,dev_address,alarm_type,ongitude,latitude):
    loop = asyncio.new_event_loop()  # 为新线程创建一个事件循环
    asyncio.set_event_loop(loop)  # 设置当前线程的事件循环为新创建的loop
    loop.run_until_complete(process_image_async(fileName,imgfile,equipment_No,shotTime,carNumber,plateColor,vehicleColor,vehicleType,speed,alarm,add_osd,type,AlarmEventType,dev_address,alarm_type,ongitude,latitude))  # 运行异步任务直到完成
    loop.close()  # 关闭事件循环
    print("Thread finished its job")

ocr_model_dir="D:\\soft\\car_regnize\\PP-OCRv5_server_rec"
# ocr_model_dir="D:\\yolov5-master\\PP-OCRv5_server_rec"
port = 9988  # Change this port if needed
server_address = ('10.180.40.101', port)  # Use '' for localhost or '0.0.0.0' for all interfaces
# server_address = ('127.0.0.1', port)  # Use '' for localhost or '0.0.0.0' for all interfaces
httpd = ThreadedHTTPServer(server_address, FileUploadHandler)

model = torch.hub.load('D:\\soft\\car_regnize', 'custom', 'D:\\soft\\car_regnize\\pt\\posicar.pt', source='local')
# model = torch.hub.load('D:\\yolov5-master', 'custom', 'D:\\yolov5-master\\pt\\posicar.pt', source='local')
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
ocr_model = TextRecognition(model_name="PP-OCRv5_server_rec",model_dir=ocr_model_dir)
post_url = "http://10.180.40.100:8889/uploadImg"
# post_url = "http://192.168.2.9:9001/uploadImg"

# 使用示例
if __name__ == "__main__":
    print('Starting server, use <Ctrl-C> to stop')
    httpd.serve_forever()  # Start the server loop to handle requests in separate threads。

---

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值