实现车牌检测算法
Posted
技术标签:
【中文标题】实现车牌检测算法【英文标题】:Implementing a License plate detection algorithm 【发布时间】:2018-06-10 18:35:24 【问题描述】:为了提高我的成像知识并获得一些处理这些主题的经验,我决定在 android 平台上创建一个车牌识别算法。
第一步是检测,为此我决定实现最近的一篇题为"A Robust and Efficient Approach to License Plate Detection" 的论文。论文很好地展示了他们的想法,并使用了非常简单的技术来实现检测。除了论文中缺少的一些细节外,我还实现了双线性下采样、转换为灰度以及边缘 + 自适应阈值处理,如第 3A、3B.1 和 3B.2 节所述。 不幸的是,我没有得到本文提出的输出,例如图 3 和 6。
我用来测试的图片如下:
灰度(和下采样)版本看起来不错(实际实现见这篇文章的底部),我使用了众所周知的 RGB 组件组合来生成它(论文没有提到如何,所以我拿了猜测)。
接下来是使用概述的 Sobel 滤波器进行的初始边缘检测。这会产生与论文图 6 中呈现的图像相似的图像。
最后,他们使用 20x20 窗口应用自适应阈值处理来移除“弱边缘”。这就是出问题的地方错误。
如您所见,即使我使用了它们声明的参数值,它也无法正常工作。另外我试过:
更改 beta 参数。 使用 2d int 数组而不是 Bitmap 对象来简化创建完整图像的过程。 尝试更高的 Gamma 参数,以便初始边缘检测允许更多“边缘”。 将窗口更改为例如10x10。然而,没有任何更改带来改进;它不断产生如上图所示的图像。我的问题是:我在做什么与论文中概述的不同?以及如何获得所需的输出?
代码
我使用的(清理过的)代码:
public int[][] toGrayscale(Bitmap bmpOriginal)
int width = bmpOriginal.getWidth();
int height = bmpOriginal.getHeight();
// color information
int A, R, G, B;
int pixel;
int[][] greys = new int[width][height];
// scan through all pixels
for (int x = 0; x < width; ++x)
for (int y = 0; y < height; ++y)
// get pixel color
pixel = bmpOriginal.getPixel(x, y);
R = Color.red(pixel);
G = Color.green(pixel);
B = Color.blue(pixel);
int gray = (int) (0.2989 * R + 0.5870 * G + 0.1140 * B);
greys[x][y] = gray;
return greys;
边缘检测代码:
private int[][] detectEges(int[][] detectionBitmap)
int width = detectionBitmap.length;
int height = detectionBitmap[0].length;
int[][] edges = new int[width][height];
// Loop over all pixels in the bitmap
int c1 = 0;
int c2 = 0;
for (int y = 0; y < height; y++)
for (int x = 2; x < width -2; x++)
// Calculate d0 for each pixel
int p0 = detectionBitmap[x][y];
int p1 = detectionBitmap[x-1][y];
int p2 = detectionBitmap[x+1][y];
int p3 = detectionBitmap[x-2][y];
int p4 = detectionBitmap[x+2][y];
int d0 = Math.abs(p1 + p2 - 2*p0) + Math.abs(p3 + p4 - 2*p0);
if(d0 >= Gamma)
c1++;
edges[x][y] = Gamma;
else
c2++;
edges[x][y] = d0;
return edges;
自适应阈值的代码。 SAT实现取自here:
private int[][] AdaptiveThreshold(int[][] detectionBitmap)
// Create the integral image
processSummedAreaTable(detectionBitmap);
int width = detectionBitmap.length;
int height = detectionBitmap[0].length;
int[][] binaryImage = new int[width][height];
int white = 0;
int black = 0;
int h_w = 20; // The window size
int half = h_w/2;
// Loop over all pixels in the bitmap
for (int y = half; y < height - half; y++)
for (int x = half; x < width - half; x++)
// Calculate d0 for each pixel
int sum = 0;
for(int k = -half; k < half - 1; k++)
for (int j = -half; j < half - 1; j++)
sum += detectionBitmap[x + k][y + j];
if(detectionBitmap[x][y] >= (sum / (h_w * h_w)) * Beta)
binaryImage[x][y] = 255;
white++;
else
binaryImage[x][y] = 0;
black++;
return binaryImage;
/**
* Process given matrix into its summed area table (in-place)
* O(MN) time, O(1) space
* @param matrix source matrix
*/
private void processSummedAreaTable(int[][] matrix)
int rowSize = matrix.length;
int colSize = matrix[0].length;
for (int i=0; i<rowSize; i++)
for (int j=0; j<colSize; j++)
matrix[i][j] = getVal(i, j, matrix);
/**
* Helper method for processSummedAreaTable
* @param row current row number
* @param col current column number
* @param matrix source matrix
* @return sub-matrix sum
*/
private int getVal (int row, int col, int[][] matrix)
int leftSum; // sub matrix sum of left matrix
int topSum; // sub matrix sum of top matrix
int topLeftSum; // sub matrix sum of top left matrix
int curr = matrix[row][col]; // current cell value
/* top left value is itself */
if (row == 0 && col == 0)
return curr;
/* top row */
else if (row == 0)
leftSum = matrix[row][col - 1];
return curr + leftSum;
/* left-most column */
if (col == 0)
topSum = matrix[row - 1][col];
return curr + topSum;
else
leftSum = matrix[row][col - 1];
topSum = matrix[row - 1][col];
topLeftSum = matrix[row - 1][col - 1]; // overlap between leftSum and topSum
return curr + leftSum + topSum - topLeftSum;
【问题讨论】:
你知道one of the authors网上有对应的源码吗? TL;DR:我们不会阅读整篇论文以及您的源代码来为您调试。对于像 SO 这样的平台来说,这实在是太耗时了。对不起,伙计,你自己一个人。 我看不出自适应阈值中的for (int y = half; y < height - half; y++)...
将如何遍历整个图像。但话又说回来,我不知道half
(或h_w
)是如何被初始化的。
@Turing85 他们没有。他们有在线可执行代码,即 .msi 和 .exe。不幸的是,源代码不可用。否则我显然会在那里看一下(不必猜测他们如何对图像进行灰度化)。
您的自适应阈值算法似乎没问题。去掉对processSummedAreaTable
的调用,我认为它计算的是积分图像?您正在尝试限制其输出,但这是行不通的。
您可以使用积分图像来加快局部阈值的计算(避免k和j上的循环),但是您需要对梯度图像进行阈值,而不是对其积分。
【参考方案1】:
Marvin 提供了一种查找文本区域的方法。也许它可以成为您的起点:
在图像中查找文本区域: http://marvinproject.sourceforge.net/en/examples/findTextRegions.html
本题也使用了这种方法:How do I separates text region from image in java
使用您的图像,我得到了以下输出:
源代码:
package textRegions;
import static marvin.MarvinPluginCollection.findTextRegions;
import java.awt.Color;
import java.util.List;
import marvin.image.MarvinImage;
import marvin.image.MarvinSegment;
import marvin.io.MarvinImageIO;
public class FindVehiclePlate
public FindVehiclePlate()
MarvinImage image = MarvinImageIO.loadImage("./res/vehicle.jpg");
image = findText(image, 30, 20, 100, 170);
MarvinImageIO.saveImage(image, "./res/vehicle_out.png");
public MarvinImage findText(MarvinImage image, int maxWhiteSpace, int maxFontLineWidth, int minTextWidth, int grayScaleThreshold)
List<MarvinSegment> segments = findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextWidth, grayScaleThreshold);
for(MarvinSegment s:segments)
if(s.height >= 10)
s.y1-=20;
s.y2+=20;
image.drawRect(s.x1, s.y1, s.x2-s.x1, s.y2-s.y1, Color.red);
image.drawRect(s.x1+1, s.y1+1, (s.x2-s.x1)-2, (s.y2-s.y1)-2, Color.red);
image.drawRect(s.x1+2, s.y1+2, (s.x2-s.x1)-4, (s.y2-s.y1)-4, Color.red);
return image;
public static void main(String[] args)
new FindVehiclePlate();
【讨论】:
以上是关于实现车牌检测算法的主要内容,如果未能解决你的问题,请参考以下文章
智能驾驶 车牌检测和识别《Android实现车牌检测和识别(可实时车牌识别)》
智能驾驶 车牌检测和识别《Android实现车牌检测和识别(可实时车牌识别)》