Python Opencv实践 - 车牌定位(纯练手,存在失败场景,可以继续优化)

        使用传统的计算机视觉方法定位图像中的车牌,参考了部分网上的文章,实际定位效果对于我目前使用的网上的图片来说还可以。实测发现对于车身本身是蓝色、或是车牌本身上方有明显边缘的情况这类图片定位效果较差。纯练手项目,仅供参考。代码中imagePreProcess对某些图片定位率相比于imagePreProcess2做预处理的效果要好。后续可以尝试做一个如果imagePreProcess2识别无效后使用imagePreProcess再处理,或者加上阈值自适应打分的机制优化。目前对于我做的练手项目来说足够了。

        注意:以下代码是参考了网上的一些文章后,按照自己的思路写的,定位效果尚可。参考的文章有:python-opencv实战:车牌识别(一):精度还不错的车牌定位_基于阈值分割的车牌定位识别-CSDN博客

 https://www.cnblogs.com/fyunaru/p/12083856.html

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

#过滤矩形的参数
minRectW = 100
minRectH = 50
#判断车牌颜色的参数
#一般情况下,蓝色车牌H分量的值通常都在115附近徘徊
# S分量和V分量因光照不同而差异较大(opencv中H分量的取值范围是0到179,而不是图像学中的0到360;S分量和V分量的取值范围是到255)
deltaH = 15
hsvLower = np.array([115 - deltaH,60,60])
hsvUpper = np.array([115 + deltaH,255,255])

#灰度拉伸
def grayScaleStretch(img):
    maxGray = float(img.max())
    minGray = float(img.min())
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            img[i,j] = 255 / (maxGray - minGray) * (img[i,j] - minGray)
    return img

#图像二值化
def image2Binary(img):
    #选取灰度最大最小值的中间值
    maxGray = float(img.max())
    minGray = float(img.min())
    threshold = (minGray + maxGray) / 2
    ret,bin = cv.threshold(img, threshold, 255, cv.THRESH_BINARY)
    return bin

#图像预处理
def imagePreProcess(img):
    #转换为灰度图
    imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    #灰度拉伸
    imgGray = grayScaleStretch(imgGray)
    #plt.imshow(imgGray, cmap='gray')
    kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3,3))
    #做开运算
    imgOpen = cv.morphologyEx(imgGray, cv.MORPH_OPEN, kernel)
    #plt.imshow(imgOpen, cmap='gray')
    #获得差分图
    imgDiff = cv.absdiff(imgGray, imgOpen)
    #plt.imshow(imgDiff, cmap='gray')
    imgDiff = cv.GaussianBlur(imgDiff, (3,3), 5)
    #plt.imshow(imgDiff, cmap='gray')
    #图像二值化
    imgBinary = image2Binary(imgDiff)
    #plt.imshow(imgBinary, cmap='gray')
    cannyEdges = cv.Canny(imgBinary, 127, 200)
    #plt.imshow(cannyEdges, cmap='gray')
    #对Canny检测边缘结果做处理
    kernel = np.ones((3,3), np.uint8)
    imgOut = cv.morphologyEx(cannyEdges, cv.MORPH_CLOSE, kernel)
    imgOut = cv.dilate(imgOut, kernel, iterations=1)
    imgOut = cv.morphologyEx(imgOut, cv.MORPH_OPEN, kernel)
    #imgOut = cv.erode(imgOut, kernel, iterations=1)
    imgOut = cv.morphologyEx(imgOut, cv.MORPH_CLOSE, kernel)
    #plt.imshow(imgOut, cmap='gray')
    return imgOut

#图像预处理2 - 对于某些
def imagePreProcess2(img):
    imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    #灰度拉伸
    imgGray = grayScaleStretch(imgGray)
    imgGray = cv.GaussianBlur(imgGray, (3,3), 5)
    #进行边缘检测
    cannyEdges = cv.Canny(imgGray, 180, 230)
    #二值化
    imgBinary = image2Binary(cannyEdges)
    #plt.imshow(imgBinary, cmap='gray')
    #先做闭运算再做开运算
    kernel = np.ones((3,3), np.uint8)
    imgOut = cv.morphologyEx(imgBinary, cv.MORPH_CLOSE, kernel)
    imgOut = cv.morphologyEx(imgOut, cv.MORPH_OPEN, kernel)
    imgOut = cv.absdiff(imgBinary, imgOut)
    imgOut = cv.morphologyEx(imgOut, cv.MORPH_CLOSE, kernel)
    imgOut = cv.dilate(imgOut, kernel, iterations=1)
    plt.imshow(imgOut, cmap='gray')
    return imgOut

#debug
def printHSV(hsvSrc):
    for i in range(hsvSrc.shape[0]):
        for j in range(hsvSrc.shape[1]):
            (h,s,v) = hsvSrc[i][j]
            print(h,s,v)

#定位车牌
def locate_plate(imgProcessing, imgOriginal):
    contours,hierarchy = cv.findContours(imgProcessing, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    carPlateCandidates = []
    for contour in contours:
        (x,y,w,h) = cv.boundingRect(contour)
        #过滤掉一些小的矩形
        if (w < minRectW or h < minRectH):
            continue
        #cv.rectangle(imgOriginal, (int(x), int(y)), (int(x + w),int(y + h)), (0,255,0), 2)
        carPlateCandidates.append([int(x),int(y),int(x + w),int(y + h)])
    
    #plt.imshow(imgOriginal[:,:,::-1])
    maxMean = 0
    target = []
    target_mask = []
    #依次检查候选车牌列表,用HSV颜色空间判别是否是车牌
    for candidate in carPlateCandidates:
        (x0,y0,x1,y1) = candidate
        candidateROI = imgOriginal[y0:y1,x0:x1]
        hsvROI = cv.cvtColor(candidateROI, cv.COLOR_BGR2HSV)
        mask = cv.inRange(hsvROI, hsvLower, hsvUpper)
        #print(mask)
        #plt.imshow(mask, cmap='gray')
        #使用均值找出蓝色最多的区域
        mean = cv.mean(mask)
        #print(mean)
        if mean[0] > maxMean:
            maxMean = mean[0]
            target = candidate
            target_mask = mask
    #对target的范围进行缩小,找出蓝色刚开始和结束的坐标
    print(target_mask)
    nonZeroPoints = cv.findNonZero(target_mask)
    #print(nonZeroPoints)
    sortByX = np.sort(nonZeroPoints, axis=0)
    xMin = sortByX[0][0][0]
    xMax = sortByX[-1][0][0]
    print(sortByX)
    sortByY = np.sort(nonZeroPoints, axis=1)
    yMin = sortByY[0][0][1]
    yMax = sortByY[-1][0][1]
    print(sortByY)


    print("X min:" + str(xMin) + " X max:" + str(xMax) + " Y min:" + str(yMin) + " Y max:" + str(yMax))
    (x0,y0,x1,y1) = target
    print("Original:" + str(x0) + "," + str(y0) + "," + str(x1) + "," + str(y1))
    #target = (x0 + xMin, y0 + yMin, x0 + (xMax - xMin), y0 + yMax - yMin)
    target = [x0 + xMin, y0 + yMin, x0 + xMax, y0 + yMax]
    return target

#读取图像
imgCarPlate = cv.imread("../../SampleImages/carplate/carplate_chongqing.jpg", cv.IMREAD_COLOR)
#plt.imshow(imgCarPlate[:,:,::-1])
img4locate = imagePreProcess2(imgCarPlate)
target = locate_plate(img4locate, imgCarPlate)
(x0,y0,x1,y1) = target
cv.rectangle(imgCarPlate, (x0,y0), (x1,y1), (0,255,0), 2)
plt.imshow(imgCarPlate[:,:,::-1])

成功的例子: 

 

 

 

 

 

不太成功的例子(轮廓检测的不太好,并且轮廓中蓝色的值过早出现,可以优化判断为连续的蓝色而不是零散的蓝色)

失败的例子(没能检测出小轮廓,车身本身为蓝色,替换为imagePreProcess后能够成功):

         


http://www.niftyadmin.cn/n/5166393.html

相关文章

毅速丨为什么不锈钢材料在金属3D打印中应用广泛

不锈钢材料作为一种常见材料&#xff0c;在金属3D打印中应用广泛&#xff0c;可以说是目前使用率最高的材料&#xff0c;为什么不锈钢大受欢迎&#xff0c;主要由几点原因。 第一、工艺适合性 金属3D打印的工艺&#xff0c;如直接金属激光烧结&#xff08;DMLS&#xff09;或选…

μC/OS-II---内核:任务调度

目录 内核&#xff1a;调度&#xff08;oc_core.c文件的函数&#xff09;OS_TCB&#xff08;任务控制块&#xff09;初始化任务控制块列表(ucos_ii.h文件的函数)系统调用&#xff0c;主动让渡CPU发生中断&#xff0c;强制当前任务让渡CPU就绪表(ucos_ii.h文件的函数)设置任务进…

功率放大器模块工作原理介绍

功率放大器模块是一种用于增强信号功率的电子设备&#xff0c;通常应用于无线通信、雷达和医疗设备等领域。它的主要作用是将输入信号放大到足够的水平&#xff0c;以便实现更高的输出功率。在本文中&#xff0c;我们将介绍功率放大器模块的工作原理。 图&#xff1a;ATA-M210高…

【hcie-cloud】【3】华为云Stack规划设计之华为云Stack交付综述【上】

文章目录 前言华为云Stack交付综述交付流程华为云Stack交付流程华为云Stack安装部署流程 交付工具链华为云Stack交付工具链eDesigner - 让解决方案销售更智能eDesigner配置页面 - 基本信息eDesigner配置页面 - 服务及组网配置eDesigner配置页面 - 弹性云服务器/ECSeDesigner配置…

UPLOAD-LABS1

less1 (js验证) 我们上传PHP的发现不可以&#xff0c;只能是jpg&#xff0c;png&#xff0c;gif&#xff08;白名单限制了&#xff09; 我们可以直接去修改限制 在查看器中看到使用了onsubmit这个函数&#xff0c;触发了鼠标的单击事件&#xff0c;在表单提交后马上调用了re…

ByteBuf和ByteBuffer

一、背景简介 ByteBuf&#xff0c;顾名思义&#xff0c;就是字节缓冲区&#xff0c;是Netty中非常重要的一个组件。熟悉jdk NIO的同学应该知道ByteBuffer&#xff0c;正是因为jdk原生ByteBuffer使用比较复杂&#xff0c;某些场景下性能不是太好&#xff0c;netty开发团队重新设…

计算机二级公共基础

知识点 1.树 树的最大层次&#xff08;最长路径的长度&#xff09;称为树的深度 二叉树的后件最多不超过两个 满二叉树&#xff1a;除最后一层每一层的所有节点都有两个子节点。&#xff08;满二叉树一定是完全二叉树&#xff09; 完全二叉树&#xff1a;所有节点均达到最大数…

图文解析 Nacos 配置中心的实现

目录 一、什么是 Nacos 二、配置中心的架构 三、Nacos 使用示例 &#xff08;一&#xff09;官方代码示例 &#xff08;二&#xff09;Properties 解读 &#xff08;三&#xff09;配置项的层级设计 &#xff08;四&#xff09;获取配置 &#xff08;五&#xff09;注册…