分割牌照字符 [英] Segmenting License Plate Characters

查看:307
本文介绍了分割牌照字符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在分析车牌图片中的字元时遇到问题。
我已经应用以下方法提取车牌字符,

  -adaptive threshold车牌图像。 
- 选择具有特定长宽比的轮廓。

如果附件中有牌照图像的阴影。
由于不正确的二进制化,我不能正确地分割字符。
图像中的阴影合并图像中的相邻字符。



我已经为不同窗口大小的图像设置了阈值,
结果附加, p>

 如果图像中有阴影,如何分割图像中的字符。 
(我使用opencv)









我在opencv中使用以下函数来限制我的车牌图像。

  cvAdaptiveThreshold(licensePlateImg,threshImg,255,CV_ADAPTIVE_THRESH_GAUSSIAN_C,CV_THRESH_BINARY_INV,wind); 

我尝试过不同的窗口大小( wind )和不同的adaptiveMethod( ADAPTIVE_THRESH_MEAN_C和ADAPTIVE_THRESH_GAUSSIAN_C
以获取阈值图像

解决方案

在我开始之前,我知道你正在寻求在OpenCV C ++中实现这个算法,但我的算法需要FFT和 numpy / scipy 真棒的。因此,我将使用Python在OpenCV中实现该算法。该代码实际上非常类似于C ++ API,您可以轻松地转录这个代替。这样,它最大限度地减少了我学习(或者重新学习...)我需要的时间量,我宁愿给你的算法和我做的任务执行这个任务,不浪费任何时间。



因此,我会给大家一个概述我会做什么。然后我将向您展示使用 numpy,scipy 和OpenCV包的Python代码。作为使用MATLAB的人的奖励,我将向您展示MATLAB代码,使用MATLAB代码启动!






您可以尝试使用同态过滤。在基本术语中,我们可以用照明和反射的乘积表示图像。照明假设是缓慢变化的,并且是动态范围的主要贡献者。这实质上是低频内容。反射表示对象的细节,并假设快速变化。这也是局部对比度的主要贡献者,并且本质上是高频率内容。



图像可以表示为这两个的产品。同态过滤尝试并分割这些组件,我们单独过滤它们。当我们完成后,我们将结果组合在一起。由于这是乘法模型,因此通常使用日志操作,以便我们可以将产品表示为两个项的和。这两个项被单独过滤,缩放以强调或不强调它们对图像的贡献,求和,然后采取反对数。



阴影是由于照明,所以我们可以做的是减少这个阴影对图像的贡献。我们还可以提高反射率,使得我们可以得到一些更好的边缘,因为边缘与高频信息相关联。



我们通常使用低通滤波器过滤照明,而使用高通滤波器过滤照明。在这种情况下,我将选择一个σ为10的高斯内核作为低通滤波器。可以通过使用 1 并用低通滤波器减去来获得高通滤波器。我将图像转换为对数域,然后使用低通和高通滤波器在频域中过滤图像。然后我缩放低通和高通结果,添加这些组件回来,然后采取反日志。此图像现在更适合于阈值化,因为图像具有低变化。



我做额外的后处理是,我阈值的图像。字母比整体背景暗,因此低于某个阈值的任何像素将被分类为文本。我选择了阈值为强度65.此后,我还清除了接触边框的所有像素,然后删除图像中任何具有总面积小于160(MATLAB)或120(Python)像素的区域。我也画出了图像的一些列,因为他们不需要我们的分析。






这里有几个注意事项:



注意事项1 - 删除边框



删除接触边框的所有像素均为内置于OpenCV中。但是,MATLAB有一个等效的名为 imclearborder 。我将在我的MATLAB代码中使用这个,但对于OpenCV,这是以下算法:




  • 查找图像中的所有轮廓

  • 对于图片中的每个轮廓,请检查是否有任何轮廓像素位于图片边框内


  • $ b


$ b

我在代码中创建了一个名为 imclearborder(imgBW,radius)的方法,其中 radius



注意事项#2 - 移除特定区域下方的像素区域



移除所有小于一定金额的区域也是在OpenCV中实施的。在MATLAB中,使用 bwareaopen 。基本算法为:




  • 查找图像中的所有轮廓


  • 任何小于一定量的区域,通过用黑色填充内部来清除这个轮廓



我创建了一个名为 bwareaopen(imgBW)的方法。



Caveat#3 - 用于删除像素区域的面积参数



对于Python代码,我不得不使用此参数和I结果为120. 160用于MATLAB。对于python,120摆脱了一些字符,这是不希望的。我猜我的实现 bwareaopen 相比,MATLAB是不同的,这可能是为什么我得到不同的结果。






不用多说,这里是代码。请注意,我没有使用空间过滤。您可以在OpenCV中使用 filter2D ,并将此图像与高斯内核进行卷积,但是我没有这样做,因为使用低通和高通滤波器的同构滤波传统上在频域中。您可以使用空间过滤进行探索,但您还必须先了解内核的大小。使用频域过滤,你只需要知道过滤器的标准偏差,这只是一个参数与两个比较。



此外,对于Python代码,我将您的图像下载到我的计算机并运行脚本。对于MATLAB,当使用图像处理工具箱读取时,可以直接引用该图像的超链接。






Python代码



  import cv2#用于OpenCV模块(用于映像I / O和轮廓查找)
import numpy as np#For通用数组操作
import scipy.fftpack#对于FFT2

#### imclearborder定义

def imclearborder(imgBW,radius):

#给定一个黑白图像,首先找到它的所有轮廓
imgBWcopy = imgBW.copy()
contoururs,hierarchy = cv2.findContours(imgBWcopy.copy(),cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)

#获取图像的大小
imgRows = imgBW.shape [0]
imgCols = imgBW.shape [1]

contourList = []#ID触摸边框的轮廓列表

#对于每个轮廓...
用于np.arange(len(contour))中的idx:
#获取第i个轮廓
cnt = contoururs [idx]

#查看轮廓中的每个点
用于cnt中的pt:
rowCnt = pt [0] [1]
colCnt = pt [0] [0]

#如果这在边界半径内

check1 =(rowCnt> = 0和rowCnt< radius)或(rowCnt> = imgRows-1-radius and rowCnt< imgRows)
check2 =(colCnt> = 0 and colCnt& ; radius)或(colCnt> = imgCols-1-radius和colCnt< imgCols)

如果check1或check2:
contourList.append(idx)
break

用于在contourList中的idx:
cv2.drawContours(imgBWcopy,contours,idx,(0,0,0),-1)

return imgBWcopy
给定一个黑白图像,首先找到它的所有轮廓
imgBWcopy = imgBW.copy(imgBW,areaPixels):
#### bwareaopen定义
def bwareaopen )
轮廓,hierarchy = cv2.findContours(imgBWcopy.copy(),cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)

#对于每个轮廓,确定其总占用面积
用于np.arange(len(轮廓))中的idx:
area = cv2.contourArea(contours [idx])
if(area> = 0 and area< = areaPixels):
cv2.drawContours(imgBWcopy,contours,idx,(0,0,0),-1)

return imgBWcopy

####主程序

#读入图像
img = cv2.imread('5DnwY.jpg',0)

#行数和列数
rows = img .shape [0]
cols = img.shape [1]

#从头和尾删除一些列
img = img [:, 59:cols-20]

#行数和列数
rows = img.shape [0]
cols = img.shape [1]

#将图像转换为0到1,然后做log(1 + I)
imgLog = np.log1p(np.array(img,dtype =float)/ 255)

# sigma = 10
M = 2 * rows + 1
N = 2 * cols + 1
sigma = 10
(X,Y)= np.meshgrid 0,N-1,N),np.linspace(0,M-1,M))
centerX = np.ceil(N / 2)
centerY = np.ceil(M /
gaussianNumerator =(X - centerX)** 2 +(Y - centerY)** 2

#低通和高通滤波器
Hlow = np.exp /(2 * sigma * sigma))
Hhigh = 1 - Hlow

#将滤波器的原点移动到左上角
#与输入图像匹配
HlowShift = scipy.fftpack.ifftshift(Hlow.copy())
HhighShift = scipy.fftpack.ifftshift(Hhigh.copy())

#过滤图像和裁剪
If = scipy.fftpack.fft2(imgLog.copy(),(M,N))
Ioutlow = scipy.real(scipy.fftpack.ifft2(If.copy()* HlowShift, ,N)))
Iouthigh = scipy.real(scipy.fftpack.ifft2(If.copy()* HhighShift,(M,N)))

#设置缩放因子并添加
gamma1 = 0.3
gamma2 = 1.5
Iout = gamma1 * Ioutlow [0:rows,0:cols] + gamma2 * Iouthigh [0:rows,0:cols]

#反日志然后重新缩放到[0,1]
Ihmf = np.expm1(Iout)
Ihmf =(Ihmf - np.min(Ihmf))/(np.max ) - np.min(Ihmf))
Ihmf2 = np.array(255 * Ihmf,dtype =uint8)

#阈值图像 - 任何低于强度65的设置为白色
Ithresh = Ihmf2< 65
Ithresh = 255 * Ithresh.astype(uint8)

#清除边框。选择一个5像素的边框半径
Iclear = imclearborder(Ithresh,5)

#消除面积小于120像素的区域
Iopen = bwareaopen(Iclear,120)

#显示所有图片
cv2.imshow('Original Image',img)
cv2.imshow('Homomorphic Filtered Result',Ihmf2)
cv2.imshow Thresholded Result',Ithresh)
cv2.imshow('Opened Result',Iopen)
cv2.waitKey(0)
cv2.destroyAllWindows()






MATLAB代码



 清除所有; 
close all;

%读入图像
I = imread('http://i.stack.imgur.com/5DnwY.jpg');

%从开始和结束处删除一些列
I = I(:,60:end-20);

%投入双倍并做日志。我们添加1,以避免log(0)错误。
I = im2double(I);
I = log(1 + I);

%在频域中创建高斯掩码
%我们必须指定我们的掩码是图像大小的两倍,以避免
%别名。
M = 2 * size(I,1)+ 1;
N = 2 * size(I,2)+ 1;
sigma = 10;
[X,Y] = meshgrid(1:N,1:M);
centerX = ceil(N / 2);
centerY = ceil(M / 2);
gaussianNumerator =(X - centerX)。^ 2 +(Y - centerY)。^ 2;

%低通和高通滤波器
Hlow = exp(-gaussianNumerator ./(2 * sigma。^ 2));
Hhigh = 1 - Hlow;

%将过滤器的原点移动到左上角,以匹配
%输入图像
Hlow = ifftshift(Hlow);
Hhigh = ifftshift(Hhigh);

%过滤图像,裁剪
If = fft2(I,M,N);
Ioutlow = real(ifft2(Hlow。* If));
Iouthigh = real(ifft2(Hhigh。* If));

%设置缩放因子,然后添加
gamma1 = 0.3;
gamma2 = 1.5;
Iout = gamma1 * Ioutlow(1:size(I,1),1:size(I,2))+ ...
gamma2 * Iouthigh :size(I,2));

%反日志然后重新缩放到[0,1]
Ihmf = exp(Iout) - 1;
Ihmf =(Ihmf - min(Ihmf(:))/(max(Ihmf(:)) - min

%阈值图像 - 任何低于强度65的设置为白色
Ithresh = Ihmf< 65/255;

%删除边框像素
Iclear = imclearborder(Ithresh,8);

%删除区域低于160像素的区域
Iopen = bwareaopen(Iclear,160);

%显示所有结果
figure;
subplot(4,1,1);
imshow(I);
title('Original Image');
subplot(4,1,2);
imshow(Ihmf);
title('Homomorphic Filtered Result');
subplot(4,1,3);
imshow(Ithresh);
title('Thresholded Result');
subplot(4,1,4);
imshow(Iopen);
title('Opened Result');






这是我得到的结果:



Python



请注意,我重新排列窗口,使它们在一列中对齐。 p>



MATLAB




I am facing problem in segmenting characters from license plate image. I have applied following method to extract license plate characters,

  -adaptive threshold the license plate image.
  -select contours which having particular aspect ratio.

If there is any shade in the license plate image as in attached file. I am not able to properly segment the characters due to improper binarization. Shade in the image merges adjacent characters in the image.

I have thresholded the images with different window sizes, Results are attached,

How can I segment characters from image if there is shade in the image.
(I am using opencv)

I have used following function in opencv to threshold my license plate image

cvAdaptiveThreshold(licensePlateImg, threshImg, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, wind);

I have tried with different window sizes (wind) and different adaptiveMethod(ADAPTIVE_THRESH_MEAN_C and ADAPTIVE_THRESH_GAUSSIAN_C) to get thresholded image

解决方案

Before I start, I know you are seeking an implementation of this algorithm in OpenCV C++, but my algorithm requires the FFT and the numpy / scipy packages are awesome for that. As such, I will give you an implementation of the algorithm in OpenCV using Python instead. The code is actually quite similar to the C++ API that you can easily transcribe that over instead. That way, it minimizes the amount of time it will take for me to learn (or rather relearn...) the API and I would rather give you the algorithm and the steps I did to perform this task to not waste any time at all.

As such, I will give you a general overview of what I would do. I will then show you Python code that uses numpy, scipy and the OpenCV packages. As a bonus for those who use MATLAB, I will show you the MATLAB equivalent, with MATLAB code to boot!


What you can do is try to use homomorphic filtering. In basic terms, we can represent an image in terms of a product of illumination and reflectance. Illumination is assumed to be slowly varying and the main contributor of dynamic range. This is essentially low frequency content. Reflectance represents details of objects and assumed to vary rapidly. This is also the primary contributor to local contrast and is essentially high frequency content.

The image can be represented as a product of these two. Homomorphic filtering tries and splits up these components and we filter them individually. We then combine the results together when we are finished. As this is a multiplicative model, it's customary to use a log operation so that we can express the product as a sum of two terms. These two terms are filtered individually, scaled to emphasize or de-emphasize their contributions to the image, summed, then the anti-log is taken.

The shading is due to the illumination, and so what we can do is decrease the contribution that this shading does over the image. We can also boost the reflectance so we can get some better edges as edges are associated with high frequency information.

We usually filter the illumination using a low-pass filter, while the reflectance with a high-pass filter. In this case, I'm going to choose a Gaussian kernel with a sigma of 10 as the low-pass filter. A high-pass filter can be obtained by taking 1 and subtracting with the low-pass filter. I transform the image into the log domain, then filter the image in the frequency domain using the low-pass and high-pass filters. I then scale the low pass and high pass results, add these components back, then take the anti-log. This image is now better suited to be thresholded as the image has low variation.

What I do as additional post-processing is that I threshold the image. The letters are darker than the overall background, so any pixels that are lower than a certain threshold would be classified as text. I chose the threshold to be intensity 65. After this, I also clear off any pixels that are touching the border, then remove any areas of the image that have less than 160 (MATLAB) or 120 (Python) pixels of total area. I also crop out some of the columns of the image as they are not needed for our analysis.


Here are a couple of caveats for you:

Caveat #1 - Removing borders

Removing any pixels that touch the border is not built into OpenCV. However, MATLAB has an equivalent called imclearborder. I'll use this in my MATLAB code, but for OpenCV, this was the following algorithm:

  • Find all of the contours in the image
  • For each contour that is in the image, check to see if any of the contour pixels are within the border of the image
  • If any are, mark this contour for removal
  • For each contour we want to remove, simply draw this whole contour in black

I created a method called imclearborder(imgBW, radius) in my code, where radius is how many pixels within the border you want to clear stuff up.

Caveat #2 - Removing pixel areas below a certain area

Removing any areas where they are less than a certain amount is also not implemented in OpenCV. In MATLAB, this is conveniently given using bwareaopen. The basic algorithm for this is:

  • Find all of the contours in the image
  • Analyze how much each contour's area fills up if you were to fill in the interior
  • Any areas that are less than a certain amount, clear this contour by filling the interior with black

I created a method called bwareaopen(imgBW) that does this for us.

Caveat #3 - Area parameter for removing pixel areas

For the Python code, I had to play around with this parameter and I settled for 120. 160 was used for MATLAB. For python, 120 got rid of some of the characters, which is not desired. I'm guessing my implementation of bwareaopen in comparison to MATLAB's is different, which is probably why I'm getting different results.


Without further ado, here's the code. Take note that I did not use spatial filtering. You could use filter2D in OpenCV and convolve this image with the Gaussian kernel, but I did not do that as Homomorphic Filtering when using low-pass and high-pass filters are traditionally done in the frequency domain. You could explore this using spatial filtering, but you would also have to know the size of your kernels before hand. With frequency domain filtering, you just need to know the standard deviation of the filter, and that's just one parameter in comparison to two.

Also, for the Python code, I downloaded your image on to my computer and ran the script. For MATLAB, you can directly reference the hyperlink to the image when reading it in with the Image Processing toolbox.


Python code

import cv2 # For OpenCV modules (For Image I/O and Contour Finding)
import numpy as np # For general purpose array manipulation
import scipy.fftpack # For FFT2 

#### imclearborder definition

def imclearborder(imgBW, radius):

    # Given a black and white image, first find all of its contours
    imgBWcopy = imgBW.copy()
    contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, 
        cv2.CHAIN_APPROX_SIMPLE)

    # Get dimensions of image
    imgRows = imgBW.shape[0]
    imgCols = imgBW.shape[1]    

    contourList = [] # ID list of contours that touch the border

    # For each contour...
    for idx in np.arange(len(contours)):
        # Get the i'th contour
        cnt = contours[idx]

        # Look at each point in the contour
        for pt in cnt:
            rowCnt = pt[0][1]
            colCnt = pt[0][0]

            # If this is within the radius of the border
            # this contour goes bye bye!
            check1 = (rowCnt >= 0 and rowCnt < radius) or (rowCnt >= imgRows-1-radius and rowCnt < imgRows)
            check2 = (colCnt >= 0 and colCnt < radius) or (colCnt >= imgCols-1-radius and colCnt < imgCols)

            if check1 or check2:
                contourList.append(idx)
                break

    for idx in contourList:
        cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)

    return imgBWcopy

#### bwareaopen definition
def bwareaopen(imgBW, areaPixels):
    # Given a black and white image, first find all of its contours
    imgBWcopy = imgBW.copy()
    contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, 
        cv2.CHAIN_APPROX_SIMPLE)

    # For each contour, determine its total occupying area
    for idx in np.arange(len(contours)):
        area = cv2.contourArea(contours[idx])
        if (area >= 0 and area <= areaPixels):
            cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)

    return imgBWcopy

#### Main program

# Read in image
img = cv2.imread('5DnwY.jpg', 0)

# Number of rows and columns
rows = img.shape[0]
cols = img.shape[1]

# Remove some columns from the beginning and end
img = img[:, 59:cols-20]

# Number of rows and columns
rows = img.shape[0]
cols = img.shape[1]

# Convert image to 0 to 1, then do log(1 + I)
imgLog = np.log1p(np.array(img, dtype="float") / 255)

# Create Gaussian mask of sigma = 10
M = 2*rows + 1
N = 2*cols + 1
sigma = 10
(X,Y) = np.meshgrid(np.linspace(0,N-1,N), np.linspace(0,M-1,M))
centerX = np.ceil(N/2)
centerY = np.ceil(M/2)
gaussianNumerator = (X - centerX)**2 + (Y - centerY)**2

# Low pass and high pass filters
Hlow = np.exp(-gaussianNumerator / (2*sigma*sigma))
Hhigh = 1 - Hlow

# Move origin of filters so that it's at the top left corner to
# match with the input image
HlowShift = scipy.fftpack.ifftshift(Hlow.copy())
HhighShift = scipy.fftpack.ifftshift(Hhigh.copy())

# Filter the image and crop
If = scipy.fftpack.fft2(imgLog.copy(), (M,N))
Ioutlow = scipy.real(scipy.fftpack.ifft2(If.copy() * HlowShift, (M,N)))
Iouthigh = scipy.real(scipy.fftpack.ifft2(If.copy() * HhighShift, (M,N)))

# Set scaling factors and add
gamma1 = 0.3
gamma2 = 1.5
Iout = gamma1*Ioutlow[0:rows,0:cols] + gamma2*Iouthigh[0:rows,0:cols]

# Anti-log then rescale to [0,1]
Ihmf = np.expm1(Iout)
Ihmf = (Ihmf - np.min(Ihmf)) / (np.max(Ihmf) - np.min(Ihmf))
Ihmf2 = np.array(255*Ihmf, dtype="uint8")

# Threshold the image - Anything below intensity 65 gets set to white
Ithresh = Ihmf2 < 65
Ithresh = 255*Ithresh.astype("uint8")

# Clear off the border.  Choose a border radius of 5 pixels
Iclear = imclearborder(Ithresh, 5)

# Eliminate regions that have areas below 120 pixels
Iopen = bwareaopen(Iclear, 120)

# Show all images
cv2.imshow('Original Image', img)
cv2.imshow('Homomorphic Filtered Result', Ihmf2)
cv2.imshow('Thresholded Result', Ithresh)
cv2.imshow('Opened Result', Iopen)
cv2.waitKey(0)
cv2.destroyAllWindows()


MATLAB code

clear all;
close all;

% Read in image
I = imread('http://i.stack.imgur.com/5DnwY.jpg');

% Remove some columns from the beginning and end
I = I(:,60:end-20);

% Cast to double and do log.  We add with 1 to avoid log(0) error.
I = im2double(I);
I = log(1 + I);

% Create Gaussian mask in frequency domain
% We must specify our mask to be twice the size of the image to avoid
% aliasing.
M = 2*size(I,1) + 1;
N = 2*size(I,2) + 1;
sigma = 10;
[X, Y] = meshgrid(1:N,1:M);
centerX = ceil(N/2);
centerY = ceil(M/2);
gaussianNumerator = (X - centerX).^2 + (Y - centerY).^2;

% Low pass and high pass filters
Hlow = exp(-gaussianNumerator./(2*sigma.^2));
Hhigh = 1 - Hlow;

% Move origin of filters so that it's at the top left corner to match with
% input image
Hlow = ifftshift(Hlow);
Hhigh = ifftshift(Hhigh);

% Filter the image, and crop
If = fft2(I, M, N);
Ioutlow = real(ifft2(Hlow .* If));
Iouthigh = real(ifft2(Hhigh .* If));

% Set scaling factors then add
gamma1 = 0.3;
gamma2 = 1.5;
Iout = gamma1*Ioutlow(1:size(I,1),1:size(I,2)) + ...
       gamma2*Iouthigh(1:size(I,1),1:size(I,2));

% Anti-log then rescale to [0,1]
Ihmf = exp(Iout) - 1;
Ihmf = (Ihmf - min(Ihmf(:))) / (max(Ihmf(:)) - min(Ihmf(:)));

% Threshold the image - Anything below intensity 65 gets set to white
Ithresh = Ihmf < 65/255;

% Remove border pixels
Iclear = imclearborder(Ithresh, 8);

% Eliminate regions that have areas below 160 pixels
Iopen = bwareaopen(Iclear, 160);

% Show all of the results
figure;
subplot(4,1,1);
imshow(I);
title('Original Image');
subplot(4,1,2);
imshow(Ihmf);
title('Homomorphic Filtered Result');
subplot(4,1,3);
imshow(Ithresh);
title('Thresholded Result');
subplot(4,1,4);
imshow(Iopen);
title('Opened Result');


This is the result I get:

Python

Take note that I re-arranged the windows so that they're aligned in a single column.

MATLAB

这篇关于分割牌照字符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆