();
148 | newSortedRect.add(chineseRect);
149 | RebuildRect(sortedRect, newSortedRect, specIndex);
150 |
151 | if (newSortedRect.size() == 0)
152 | return -3;
153 |
154 | for (int i = 0; i < newSortedRect.size(); i++) {
155 | Rect mr = newSortedRect.get(i);
156 | Mat auxRoi = new Mat(img_threshold, mr);
157 |
158 | auxRoi = preprocessChar(auxRoi);
159 | if (this.isDebug) {
160 | String str = "tmp/debug_char_auxRoi_" + Integer.valueOf(i).toString() + ".jpg";
161 | imwrite(str, auxRoi);
162 | }
163 | resultVec.add(auxRoi);
164 | }
165 | return 0;
166 | }
167 |
168 | /**
169 | * 字符尺寸验证
170 | *
171 | * @param r
172 | * @return
173 | */
174 | private Boolean verifySizes(Mat r) {
175 | float aspect = 45.0f / 90.0f;
176 | float charAspect = (float) r.cols() / (float) r.rows();
177 | float error = 0.7f;
178 | float minHeight = 10f;
179 | float maxHeight = 35f;
180 | // We have a different aspect ratio for number 1, and it can be ~0.2
181 | float minAspect = 0.05f;
182 | float maxAspect = aspect + aspect * error;
183 | // area of pixels
184 | float area = countNonZero(r);
185 | // bb area
186 | float bbArea = r.cols() * r.rows();
187 | // % of pixel in area
188 | float percPixels = area / bbArea;
189 |
190 | return percPixels <= 1 && charAspect > minAspect && charAspect < maxAspect && r.rows() >= minHeight
191 | && r.rows() < maxHeight;
192 | }
193 |
194 | /**
195 | * 字符预处理: 统一每个字符的大小
196 | *
197 | * @param in
198 | * @return
199 | */
200 | private Mat preprocessChar(Mat in) {
201 | int h = in.rows();
202 | int w = in.cols();
203 | int charSize = CHAR_SIZE;
204 | Mat transformMat = Mat.eye(2, 3, CV_32F).asMat();
205 | int m = Math.max(w, h);
206 | transformMat.ptr(0, 2).put(Convert.getBytes(((m - w) / 2f)));
207 | transformMat.ptr(1, 2).put(Convert.getBytes((m - h) / 2f));
208 |
209 | Mat warpImage = new Mat(m, m, in.type());
210 | warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, new Scalar(0));
211 |
212 | Mat out = new Mat();
213 | resize(warpImage, out, new Size(charSize, charSize));
214 |
215 | return out;
216 | }
217 |
218 | /**
219 | * 去除车牌上方的钮钉
220 | *
221 | * 计算每行元素的阶跃数,如果小于X认为是柳丁,将此行全部填0(涂黑), X可根据实际调整
222 | *
223 | * @param img
224 | * @return
225 | */
226 | private Mat clearLiuDing(Mat img) {
227 | final int x = this.liuDingSize;
228 |
229 | Mat jump = Mat.zeros(1, img.rows(), CV_32F).asMat();
230 | for (int i = 0; i < img.rows(); i++) {
231 | int jumpCount = 0;
232 | for (int j = 0; j < img.cols() - 1; j++) {
233 | if (img.ptr(i, j).get() != img.ptr(i, j + 1).get())
234 | jumpCount++;
235 | }
236 | jump.ptr(i).put(Convert.getBytes((float) jumpCount));
237 | }
238 | for (int i = 0; i < img.rows(); i++) {
239 | if (Convert.toFloat(jump.ptr(i)) <= x) {
240 | for (int j = 0; j < img.cols(); j++) {
241 | img.ptr(i, j).put((byte) 0);
242 | }
243 | }
244 | }
245 | return img;
246 | }
247 |
248 | /**
249 | * 根据特殊车牌来构造猜测中文字符的位置和大小
250 | *
251 | * @param rectSpe
252 | * @return
253 | */
254 | private Rect GetChineseRect(final Rect rectSpe) {
255 | int height = rectSpe.height();
256 | float newwidth = rectSpe.width() * 1.15f;
257 | int x = rectSpe.x();
258 | int y = rectSpe.y();
259 |
260 | int newx = x - (int) (newwidth * 1.15);
261 | newx = Math.max(newx, 0);
262 | Rect a = new Rect(newx, y, (int) newwidth, height);
263 | return a;
264 | }
265 |
266 | /**
267 | * 找出指示城市的字符的Rect,例如苏A7003X,就是A的位置
268 | *
269 | * @param vecRect
270 | * @return
271 | */
272 | private int GetSpecificRect(final Vector vecRect) {
273 | Vector xpositions = new Vector();
274 | int maxHeight = 0;
275 | int maxWidth = 0;
276 | for (int i = 0; i < vecRect.size(); i++) {
277 | xpositions.add(vecRect.get(i).x());
278 |
279 | if (vecRect.get(i).height() > maxHeight) {
280 | maxHeight = vecRect.get(i).height();
281 | }
282 | if (vecRect.get(i).width() > maxWidth) {
283 | maxWidth = vecRect.get(i).width();
284 | }
285 | }
286 |
287 | int specIndex = 0;
288 | for (int i = 0; i < vecRect.size(); i++) {
289 | Rect mr = vecRect.get(i);
290 | int midx = mr.x() + mr.width() / 2;
291 |
292 | // 如果一个字符有一定的大小,并且在整个车牌的1/7到2/7之间,则是我们要找的特殊车牌
293 | if ((mr.width() > maxWidth * 0.8 || mr.height() > maxHeight * 0.8)
294 | && (midx < this.theMatWidth * 2 / 7 && midx > this.theMatWidth / 7)) {
295 | specIndex = i;
296 | }
297 | }
298 |
299 | return specIndex;
300 | }
301 |
302 | /**
303 | * 这个函数做两个事情
304 | *
305 | * - 把特殊字符Rect左边的全部Rect去掉,后面再重建中文字符的位置;
306 | *
- 从特殊字符Rect开始,依次选择6个Rect,多余的舍去。
307 | *
308 | *
309 | * @param vecRect
310 | * @param outRect
311 | * @param specIndex
312 | * @return
313 | */
314 | private int RebuildRect(final Vector vecRect, Vector outRect, int specIndex) {
315 | // 最大只能有7个Rect,减去中文的就只有6个Rect
316 | int count = 6;
317 | for (int i = 0; i < vecRect.size(); i++) {
318 | // 将特殊字符左边的Rect去掉,这个可能会去掉中文Rect,不过没关系,我们后面会重建。
319 | if (i < specIndex)
320 | continue;
321 |
322 | outRect.add(vecRect.get(i));
323 | if (--count == 0)
324 | break;
325 | }
326 |
327 | return 0;
328 | }
329 |
330 | /**
331 | * 将Rect按位置从左到右进行排序
332 | *
333 | * @param vecRect
334 | * @param out
335 | * @return
336 | */
337 | private void SortRect(final Vector vecRect, Vector out) {
338 | Vector orderIndex = new Vector();
339 | Vector xpositions = new Vector();
340 | for (int i = 0; i < vecRect.size(); ++i) {
341 | orderIndex.add(i);
342 | xpositions.add(vecRect.get(i).x());
343 | }
344 |
345 | float min = xpositions.get(0);
346 | int minIdx;
347 | for (int i = 0; i < xpositions.size(); ++i) {
348 | min = xpositions.get(i);
349 | minIdx = i;
350 | for (int j = i; j < xpositions.size(); ++j) {
351 | if (xpositions.get(j) < min) {
352 | min = xpositions.get(j);
353 | minIdx = j;
354 | }
355 | }
356 | int aux_i = orderIndex.get(i);
357 | int aux_min = orderIndex.get(minIdx);
358 | orderIndex.remove(i);
359 | orderIndex.insertElementAt(aux_min, i);
360 | orderIndex.remove(minIdx);
361 | orderIndex.insertElementAt(aux_i, minIdx);
362 |
363 | float aux_xi = xpositions.get(i);
364 | float aux_xmin = xpositions.get(minIdx);
365 | xpositions.remove(i);
366 | xpositions.insertElementAt((int) aux_xmin, i);
367 | xpositions.remove(minIdx);
368 | xpositions.insertElementAt((int) aux_xi, minIdx);
369 | }
370 |
371 | for (int i = 0; i < orderIndex.size(); i++)
372 | out.add(vecRect.get(orderIndex.get(i)));
373 |
374 | return;
375 | }
376 |
377 | public void setLiuDingSize(int param) {
378 | this.liuDingSize = param;
379 | }
380 |
381 | public void setColorThreshold(int param) {
382 | this.colorThreshold = param;
383 | }
384 |
385 | public void setBluePercent(float param) {
386 | this.bluePercent = param;
387 | }
388 |
389 | public final float getBluePercent() {
390 | return this.bluePercent;
391 | }
392 |
393 | public void setWhitePercent(float param) {
394 | this.whitePercent = param;
395 | }
396 |
397 | public final float getWhitePercent() {
398 | return this.whitePercent;
399 | }
400 |
401 | public boolean getDebug() {
402 | return this.isDebug;
403 | }
404 |
405 | public void setDebug(boolean isDebug) {
406 | this.isDebug = isDebug;
407 | }
408 |
409 | // 是否开启调试模式常量,默认false代表关闭
410 | final static boolean DEFAULT_DEBUG = true;
411 |
412 | // preprocessChar所用常量
413 | final static int CHAR_SIZE = 20;
414 | final static int HORIZONTAL = 1;
415 | final static int VERTICAL = 0;
416 |
417 | final static int DEFAULT_LIUDING_SIZE = 7;
418 | final static int DEFAULT_MAT_WIDTH = 136;
419 |
420 | final static int DEFAULT_COLORTHRESHOLD = 150;
421 | final static float DEFAULT_BLUEPERCEMT = 0.3f;
422 | final static float DEFAULT_WHITEPERCEMT = 0.1f;
423 |
424 | private int liuDingSize = DEFAULT_LIUDING_SIZE;
425 | private int theMatWidth = DEFAULT_MAT_WIDTH;
426 |
427 | private int colorThreshold = DEFAULT_COLORTHRESHOLD;
428 | private float bluePercent = DEFAULT_BLUEPERCEMT;
429 | private float whitePercent = DEFAULT_WHITEPERCEMT;
430 |
431 | private boolean isDebug = DEFAULT_DEBUG;
432 | }
433 |
--------------------------------------------------------------------------------
/src/org/easypr/core/CoreFunc.java:
--------------------------------------------------------------------------------
1 | package org.easypr.core;
2 |
3 | import static org.bytedeco.javacpp.opencv_core.*;
4 | import static org.bytedeco.javacpp.opencv_highgui.cvShowImage;
5 | import static org.bytedeco.javacpp.opencv_highgui.cvWaitKey;
6 | import static org.bytedeco.javacpp.opencv_imgproc.CV_BGR2HSV;
7 | import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
8 | import static org.bytedeco.javacpp.opencv_imgproc.equalizeHist;
9 | import static org.bytedeco.javacpp.opencv_imgproc.resize;
10 |
11 | import org.bytedeco.javacpp.BytePointer;
12 | import org.bytedeco.javacpp.opencv_core.IplImage;
13 | import org.bytedeco.javacpp.opencv_core.Mat;
14 | import org.bytedeco.javacpp.opencv_core.MatVector;
15 | import org.bytedeco.javacpp.opencv_core.Size;
16 | import org.bytedeco.javacpp.indexer.FloatIndexer;
17 |
18 | /**
19 | * @author lin.yao
20 | *
21 | */
22 | public class CoreFunc {
23 | public enum Color {
24 | UNKNOWN, BLUE, YELLOW
25 | };
26 |
27 | public enum Direction {
28 | UNKNOWN, VERTICAL, HORIZONTAL
29 | }
30 |
31 | /**
32 | * 鏍规嵁涓�骞呭浘鍍忎笌棰滆壊妯℃澘鑾峰彇瀵瑰簲鐨勪簩鍊煎浘
33 | *
34 | * @param src
35 | * 杈撳叆RGB鍥惧儚
36 | * @param r
37 | * 棰滆壊妯℃澘锛堣摑鑹层�侀粍鑹诧級
38 | * @param adaptive_minsv
39 | * S鍜孷鐨勬渶灏忓�肩敱adaptive_minsv杩欎釜bool鍊煎垽鏂�
40 | *
41 | * - 濡傛灉涓簍rue锛屽垯鏈�灏忓�煎彇鍐充簬H鍊硷紝鎸夋瘮渚嬭“鍑�
42 | *
- 濡傛灉涓篺alse锛屽垯涓嶅啀鑷�傚簲锛屼娇鐢ㄥ浐瀹氱殑鏈�灏忓�糾inabs_sv
43 | *
44 | * @return 杈撳嚭鐏板害鍥撅紙鍙湁0鍜�255涓や釜鍊硷紝255浠h〃鍖归厤锛�0浠h〃涓嶅尮閰嶏級
45 | */
46 | public static Mat colorMatch(final Mat src, final Color r, final boolean adaptive_minsv) {
47 | final float max_sv = 255;
48 | final float minref_sv = 64;
49 | final float minabs_sv = 95;
50 |
51 | // blue鐨凥鑼冨洿
52 | final int min_blue = 100;
53 | final int max_blue = 140;
54 |
55 | // yellow鐨凥鑼冨洿
56 | final int min_yellow = 15;
57 | final int max_yellow = 40;
58 |
59 | // 杞埌HSV绌洪棿杩涜澶勭悊锛岄鑹叉悳绱富瑕佷娇鐢ㄧ殑鏄疕鍒嗛噺杩涜钃濊壊涓庨粍鑹茬殑鍖归厤宸ヤ綔
60 | Mat src_hsv = new Mat();
61 | cvtColor(src, src_hsv, CV_BGR2HSV);
62 | MatVector hsvSplit = new MatVector();
63 | split(src_hsv, hsvSplit);
64 | equalizeHist(hsvSplit.get(2), hsvSplit.get(2));
65 | merge(hsvSplit, src_hsv);
66 |
67 | // 鍖归厤妯℃澘鍩鸿壊,鍒囨崲浠ユ煡鎵炬兂瑕佺殑鍩鸿壊
68 | int min_h = 0;
69 | int max_h = 0;
70 | switch (r) {
71 | case BLUE:
72 | min_h = min_blue;
73 | max_h = max_blue;
74 | break;
75 | case YELLOW:
76 | min_h = min_yellow;
77 | max_h = max_yellow;
78 | break;
79 | default:
80 | break;
81 | }
82 |
83 | float diff_h = (float) ((max_h - min_h) / 2);
84 | int avg_h = (int) (min_h + diff_h);
85 |
86 | int channels = src_hsv.channels();
87 | int nRows = src_hsv.rows();
88 | // 鍥惧儚鏁版嵁鍒楅渶瑕佽�冭檻閫氶亾鏁扮殑褰卞搷锛�
89 | int nCols = src_hsv.cols() * channels;
90 |
91 | // 杩炵画瀛樺偍鐨勬暟鎹紝鎸変竴琛屽鐞�
92 | if (src_hsv.isContinuous()) {
93 | nCols *= nRows;
94 | nRows = 1;
95 | }
96 |
97 | for (int i = 0; i < nRows; ++i) {
98 | BytePointer p = src_hsv.ptr(i);
99 | for (int j = 0; j < nCols; j += 3) {
100 | int H = p.get(j) & 0xFF;
101 | int S = p.get(j + 1) & 0xFF;
102 | int V = p.get(j + 2) & 0xFF;
103 |
104 | boolean colorMatched = false;
105 |
106 | if (H > min_h && H < max_h) {
107 | int Hdiff = 0;
108 | if (H > avg_h)
109 | Hdiff = H - avg_h;
110 | else
111 | Hdiff = avg_h - H;
112 |
113 | float Hdiff_p = Hdiff / diff_h;
114 |
115 | float min_sv = 0;
116 | if (true == adaptive_minsv)
117 | min_sv = minref_sv - minref_sv / 2 * (1 - Hdiff_p);
118 | else
119 | min_sv = minabs_sv;
120 |
121 | if ((S > min_sv && S <= max_sv) && (V > min_sv && V <= max_sv))
122 | colorMatched = true;
123 | }
124 |
125 | if (colorMatched == true) {
126 | p.put(j, (byte) 0);
127 | p.put(j + 1, (byte) 0);
128 | p.put(j + 2, (byte) 255);
129 | } else {
130 | p.put(j, (byte) 0);
131 | p.put(j + 1, (byte) 0);
132 | p.put(j + 2, (byte) 0);
133 | }
134 | }
135 | }
136 |
137 | // 鑾峰彇棰滆壊鍖归厤鍚庣殑浜屽�肩伆搴﹀浘
138 | MatVector hsvSplit_done = new MatVector();
139 | split(src_hsv, hsvSplit_done);
140 | Mat src_grey = hsvSplit_done.get(2);
141 |
142 | return src_grey;
143 | }
144 |
145 | /**
146 | * 鍒ゆ柇涓�涓溅鐗岀殑棰滆壊
147 | *
148 | * @param src
149 | * 杞︾墝mat
150 | * @param r
151 | * 棰滆壊妯℃澘
152 | * @param adaptive_minsv
153 | * S鍜孷鐨勬渶灏忓�肩敱adaptive_minsv杩欎釜bool鍊煎垽鏂�
154 | *
155 | * - 濡傛灉涓簍rue锛屽垯鏈�灏忓�煎彇鍐充簬H鍊硷紝鎸夋瘮渚嬭“鍑�
156 | *
- 濡傛灉涓篺alse锛屽垯涓嶅啀鑷�傚簲锛屼娇鐢ㄥ浐瀹氱殑鏈�灏忓�糾inabs_sv
157 | *
158 | * @return
159 | */
160 | public static boolean plateColorJudge(final Mat src, final Color color, final boolean adaptive_minsv) {
161 | // 鍒ゆ柇闃堝��
162 | final float thresh = 0.49f;
163 |
164 | Mat gray = colorMatch(src, color, adaptive_minsv);
165 |
166 | float percent = (float) countNonZero(gray) / (gray.rows() * gray.cols());
167 |
168 | return (percent > thresh) ? true : false;
169 | }
170 |
171 | /**
172 | * getPlateType 鍒ゆ柇杞︾墝鐨勭被鍨�
173 | *
174 | * @param src
175 | * @param adaptive_minsv
176 | * S鍜孷鐨勬渶灏忓�肩敱adaptive_minsv杩欎釜bool鍊煎垽鏂�
177 | *
178 | * - 濡傛灉涓簍rue锛屽垯鏈�灏忓�煎彇鍐充簬H鍊硷紝鎸夋瘮渚嬭“鍑�
179 | *
- 濡傛灉涓篺alse锛屽垯涓嶅啀鑷�傚簲锛屼娇鐢ㄥ浐瀹氱殑鏈�灏忓�糾inabs_sv
180 | *
181 | * @return
182 | */
183 | public static Color getPlateType(final Mat src, final boolean adaptive_minsv) {
184 | if (plateColorJudge(src, Color.BLUE, adaptive_minsv) == true) {
185 | return Color.BLUE;
186 | } else if (plateColorJudge(src, Color.YELLOW, adaptive_minsv) == true) {
187 | return Color.YELLOW;
188 | } else {
189 | return Color.UNKNOWN;
190 | }
191 | }
192 |
193 | /**
194 | * 鑾峰彇鍨傜洿鎴栨按骞虫柟鍚戠洿鏂瑰浘
195 | *
196 | * @param img
197 | * @param direction
198 | * @return
199 | */
200 | public static float[] projectedHistogram(final Mat img, Direction direction) {
201 | int sz = 0;
202 | switch (direction) {
203 | case HORIZONTAL:
204 | sz = img.rows();
205 | break;
206 |
207 | case VERTICAL:
208 | sz = img.cols();
209 | break;
210 |
211 | default:
212 | break;
213 | }
214 |
215 | // 缁熻杩欎竴琛屾垨涓�鍒椾腑锛岄潪闆跺厓绱犵殑涓暟锛屽苟淇濆瓨鍒皀onZeroMat涓�
216 | float[] nonZeroMat = new float[sz];
217 | extractChannel(img, img, 0);
218 | for (int j = 0; j < sz; j++) {
219 | Mat data = (direction == Direction.HORIZONTAL) ? img.row(j) : img.col(j);
220 | int count = countNonZero(data);
221 | nonZeroMat[j] = count;
222 | }
223 |
224 | // Normalize histogram
225 | float max = 0;
226 | for (int j = 0; j < nonZeroMat.length; ++j) {
227 | max = Math.max(max, nonZeroMat[j]);
228 | }
229 |
230 | if (max > 0) {
231 | for (int j = 0; j < nonZeroMat.length; ++j) {
232 | nonZeroMat[j] /= max;
233 | }
234 | }
235 |
236 | return nonZeroMat;
237 | }
238 |
239 | /**
240 | * Assign values to feature
241 | *
242 | * 鏍锋湰鐗瑰緛涓烘按骞炽�佸瀭鐩寸洿鏂瑰浘鍜屼綆鍒嗚鲸鐜囧浘鍍忔墍缁勬垚鐨勭煝閲�
243 | *
244 | * @param in
245 | * @param sizeData
246 | * 浣庡垎杈ㄧ巼鍥惧儚size = sizeData*sizeData, 鍙互涓�0
247 | * @return
248 | */
249 | public static Mat features(final Mat in, final int sizeData) {
250 |
251 | float[] vhist = projectedHistogram(in, Direction.VERTICAL);
252 | float[] hhist = projectedHistogram(in, Direction.HORIZONTAL);
253 |
254 | Mat lowData = new Mat();
255 | if (sizeData > 0) {
256 | resize(in, lowData, new Size(sizeData, sizeData));
257 | }
258 |
259 | int numCols = vhist.length + hhist.length + lowData.cols() * lowData.rows();
260 | Mat out = Mat.zeros(1, numCols, CV_32F).asMat();
261 | FloatIndexer idx = out.createIndexer();
262 |
263 | int j = 0;
264 | for (int i = 0; i < vhist.length; ++i, ++j) {
265 | idx.put(0, j, vhist[i]);
266 | }
267 | for (int i = 0; i < hhist.length; ++i, ++j) {
268 | idx.put(0, j, hhist[i]);
269 | }
270 | for (int x = 0; x < lowData.cols(); x++) {
271 | for (int y = 0; y < lowData.rows(); y++, ++j) {
272 | float val = lowData.ptr(x, y).get() & 0xFF;
273 | idx.put(0, j, val);
274 | }
275 | }
276 |
277 | return out;
278 | }
279 |
280 | /**
281 | * Show image
282 | *
283 | * @param title
284 | * @param src
285 | */
286 | public static void showImage(final String title, final Mat src) {
287 | try {
288 | IplImage image = src.asIplImage();
289 | if (image != null) {
290 | cvShowImage(title, image);
291 | cvWaitKey(0);
292 | }
293 | } catch (Exception ex) {
294 | }
295 | }
296 | }
297 |
--------------------------------------------------------------------------------
/src/org/easypr/core/Features.java:
--------------------------------------------------------------------------------
1 | package org.easypr.core;
2 |
3 | import static org.bytedeco.javacpp.opencv_core.merge;
4 | import static org.bytedeco.javacpp.opencv_core.split;
5 | import static org.bytedeco.javacpp.opencv_imgproc.*;
6 | import static org.easypr.core.CoreFunc.features;
7 |
8 | import org.bytedeco.javacpp.opencv_core.Mat;
9 | import org.bytedeco.javacpp.opencv_core.MatVector;
10 |
11 | /**
12 | *
13 | * @author Created by fanwenjie
14 | * @author lin.yao
15 | *
16 | */
17 | public class Features implements SVMCallback {
18 |
19 | /*
20 | * (non-Javadoc)
21 | *
22 | * @see org.easypr.core.SVMCallback#getHisteqFeatures(org.bytedeco.javacpp.
23 | * opencv_core.Mat)
24 | */
25 | // @Override
26 | public Mat getHisteqFeatures(final Mat image) {
27 | return histeq(image);
28 | }
29 |
30 | /*
31 | * (non-Javadoc)
32 | *
33 | * @see
34 | * org.easypr.core.SVMCallback#getHistogramFeatures(org.bytedeco.javacpp
35 | * .opencv_core.Mat)
36 | */
37 | public Mat getHistogramFeatures(Mat image) {
38 | Mat grayImage = new Mat();
39 | cvtColor(image, grayImage, CV_RGB2GRAY);
40 |
41 | Mat img_threshold = new Mat();
42 | threshold(grayImage, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
43 |
44 | return features(img_threshold, 0);
45 | }
46 |
47 | /*
48 | * (non-Javadoc)
49 | *
50 | * @see
51 | * org.easypr.core.SVMCallback#getSIFTFeatures(org.bytedeco.javacpp.opencv_core
52 | * .Mat)
53 | */
54 | public Mat getSIFTFeatures(final Mat image) {
55 | // TODO: 待完善
56 | return null;
57 | }
58 |
59 | /*
60 | * (non-Javadoc)
61 | *
62 | * @see
63 | * org.easypr.core.SVMCallback#getHOGFeatures(org.bytedeco.javacpp.opencv_core
64 | * .Mat)
65 | */
66 | public Mat getHOGFeatures(final Mat image) {
67 | // TODO: 待完善
68 | return null;
69 | }
70 |
71 | private Mat histeq(Mat in) {
72 | Mat out = new Mat(in.size(), in.type());
73 | if (in.channels() == 3) {
74 | Mat hsv = new Mat();
75 | MatVector hsvSplit = new MatVector();
76 | cvtColor(in, hsv, CV_BGR2HSV);
77 | split(hsv, hsvSplit);
78 | equalizeHist(hsvSplit.get(2), hsvSplit.get(2));
79 | merge(hsvSplit, hsv);
80 | cvtColor(hsv, out, CV_HSV2BGR);
81 | hsv = null;
82 | hsvSplit = null;
83 | System.gc();
84 | } else if (in.channels() == 1) {
85 | equalizeHist(in, out);
86 | }
87 | return out;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/org/easypr/core/PlateDetect.java:
--------------------------------------------------------------------------------
1 | package org.easypr.core;
2 |
3 | import static org.bytedeco.javacpp.opencv_highgui.imwrite;
4 |
5 | import java.util.Vector;
6 |
7 | import org.bytedeco.javacpp.opencv_core.Mat;
8 |
9 | /**
10 | * @author Created by fanwenjie
11 | * @author lin.yao
12 | *
13 | */
14 | public class PlateDetect {
15 |
16 | /**
17 | * @param src
18 | * @param resultVec
19 | * 可能是车牌的图块集合
20 | * @return the error number
21 | *
22 | * - 0: plate detected successfully;
23 | *
- -1: source Mat is empty;
24 | *
- -2: plate not detected.
25 | *
26 | */
27 | public int plateDetect(final Mat src, Vector resultVec) {
28 | Vector matVec = plateLocate.plateLocate(src);
29 |
30 | if (0 == matVec.size()) {
31 | return -1;
32 | }
33 |
34 | if (0 != plateJudge.plateJudge(matVec, resultVec)) {
35 | return -2;
36 | }
37 |
38 | if (getPDDebug()) {
39 | int size = (int) resultVec.size();
40 | for (int i = 0; i < size; i++) {
41 | Mat img = resultVec.get(i);
42 | String str = "tmp/plate_judge_result_" + Integer.valueOf(i).toString() + ".jpg";
43 | imwrite(str, img);
44 | }
45 | }
46 |
47 | return 0;
48 | }
49 |
50 | /**
51 | * 生活模式与工业模式切换
52 | *
53 | * @param pdLifemode
54 | */
55 | public void setPDLifemode(boolean pdLifemode) {
56 | plateLocate.setLifemode(pdLifemode);
57 | }
58 |
59 | /**
60 | * 是否开启调试模式
61 | *
62 | * @param pdDebug
63 | */
64 | public void setPDDebug(boolean pdDebug) {
65 | plateLocate.setDebug(pdDebug);
66 | }
67 |
68 | /**
69 | * 获取调试模式状态
70 | *
71 | * @return
72 | */
73 | public boolean getPDDebug() {
74 | return plateLocate.getDebug();
75 | }
76 |
77 | public void setGaussianBlurSize(int gaussianBlurSize) {
78 | plateLocate.setGaussianBlurSize(gaussianBlurSize);
79 | }
80 |
81 | public final int getGaussianBlurSize() {
82 | return plateLocate.getGaussianBlurSize();
83 | }
84 |
85 | public void setMorphSizeWidth(int morphSizeWidth) {
86 | plateLocate.setMorphSizeWidth(morphSizeWidth);
87 | }
88 |
89 | public final int getMorphSizeWidth() {
90 | return plateLocate.getMorphSizeWidth();
91 | }
92 |
93 | public void setMorphSizeHeight(int morphSizeHeight) {
94 | plateLocate.setMorphSizeHeight(morphSizeHeight);
95 | }
96 |
97 | public final int getMorphSizeHeight() {
98 | return plateLocate.getMorphSizeHeight();
99 | }
100 |
101 | public void setVerifyError(float verifyError) {
102 | plateLocate.setVerifyError(verifyError);
103 | }
104 |
105 | public final float getVerifyError() {
106 | return plateLocate.getVerifyError();
107 | }
108 |
109 | public void setVerifyAspect(float verifyAspect) {
110 | plateLocate.setVerifyAspect(verifyAspect);
111 | }
112 |
113 | public final float getVerifyAspect() {
114 | return plateLocate.getVerifyAspect();
115 | }
116 |
117 | public void setVerifyMin(int verifyMin) {
118 | plateLocate.setVerifyMin(verifyMin);
119 | }
120 |
121 | public void setVerifyMax(int verifyMax) {
122 | plateLocate.setVerifyMax(verifyMax);
123 | }
124 |
125 | public void setJudgeAngle(int judgeAngle) {
126 | plateLocate.setJudgeAngle(judgeAngle);
127 | }
128 |
129 | private PlateLocate plateLocate = new PlateLocate();
130 |
131 | private PlateJudge plateJudge = new PlateJudge();
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/org/easypr/core/PlateJudge.java:
--------------------------------------------------------------------------------
1 | package org.easypr.core;
2 |
3 | import static org.bytedeco.javacpp.opencv_core.CV_32FC1;
4 | import static org.bytedeco.javacpp.opencv_imgproc.resize;
5 |
6 | import java.util.Vector;
7 |
8 | import org.bytedeco.javacpp.opencv_core.Mat;
9 | import org.bytedeco.javacpp.opencv_core.Rect;
10 | import org.bytedeco.javacpp.opencv_core.Size;
11 | import org.bytedeco.javacpp.opencv_ml.CvSVM;
12 |
13 | /**
14 | * @author Created by fanwenjie
15 | * @author lin.yao
16 | *
17 | */
18 | public class PlateJudge {
19 |
20 | public PlateJudge() {
21 | loadModel();
22 | }
23 |
24 | public void loadModel() {
25 | loadModel(path);
26 | }
27 |
28 | public void loadModel(String s) {
29 | svm.clear();
30 | svm.load(s, "svm");
31 | }
32 |
33 | /**
34 | * 对单幅图像进行SVM判断
35 | *
36 | * @param inMat
37 | * @return
38 | */
39 | public int plateJudge(final Mat inMat) {
40 | Mat features = this.features.getHistogramFeatures(inMat);
41 |
42 | // 通过直方图均衡化后的彩色图进行预测
43 | Mat p = features.reshape(1, 1);
44 | p.convertTo(p, CV_32FC1);
45 | float ret = svm.predict(features);
46 |
47 | return (int) ret;
48 | }
49 |
50 | /**
51 | * 对多幅图像进行SVM判断
52 | *
53 | * @param inVec
54 | * @param resultVec
55 | * @return
56 | */
57 | public int plateJudge(Vector inVec, Vector resultVec) {
58 |
59 | for (int j = 0; j < inVec.size(); j++) {
60 | Mat inMat = inVec.get(j);
61 |
62 | if (1 == plateJudge(inMat)) {
63 | resultVec.add(inMat);
64 | } else { // 再取中间部分判断一次
65 | int w = inMat.cols();
66 | int h = inMat.rows();
67 |
68 | Mat tmpDes = inMat.clone();
69 | Mat tmpMat = new Mat(inMat, new Rect((int) (w * 0.05), (int) (h * 0.1), (int) (w * 0.9),
70 | (int) (h * 0.8)));
71 | resize(tmpMat, tmpDes, new Size(inMat.size()));
72 |
73 | if (plateJudge(tmpDes) == 1) {
74 | resultVec.add(inMat);
75 | }
76 | }
77 | }
78 |
79 | return 0;
80 | }
81 |
82 | public void setModelPath(String path) {
83 | this.path = path;
84 | }
85 |
86 | public final String getModelPath() {
87 | return path;
88 | }
89 |
90 | private CvSVM svm = new CvSVM();
91 |
92 | /**
93 | * EasyPR的getFeatures回调函数, 用于从车牌的image生成svm的训练特征features
94 | */
95 | private SVMCallback features = new Features();
96 |
97 | /**
98 | * 模型存储路径
99 | */
100 | private String path = "res/model/svm.xml";
101 | }
102 |
--------------------------------------------------------------------------------
/src/org/easypr/core/PlateLocate.java:
--------------------------------------------------------------------------------
1 | package org.easypr.core;
2 |
3 | import static org.bytedeco.javacpp.opencv_core.*;
4 | import static org.bytedeco.javacpp.opencv_highgui.imwrite;
5 | import static org.bytedeco.javacpp.opencv_imgproc.*;
6 | import org.easypr.Main;
7 | import java.util.Vector;
8 |
9 | import javax.swing.JFrame;
10 |
11 | import org.bytedeco.javacpp.opencv_core.Mat;
12 | import org.bytedeco.javacpp.opencv_core.MatVector;
13 | import org.bytedeco.javacpp.opencv_core.Point;
14 | import org.bytedeco.javacpp.opencv_core.Point2f;
15 | import org.bytedeco.javacpp.opencv_core.RotatedRect;
16 | import org.bytedeco.javacpp.opencv_core.Scalar;
17 | import org.bytedeco.javacpp.opencv_core.Size;
18 | import org.bytedeco.javacv.CanvasFrame;
19 |
20 | /**
21 | * @author Created by fanwenjie
22 | * @author lin.yao
23 | *
24 | */
25 | public class PlateLocate {
26 |
27 | /**
28 | * 生活模式与工业模式切换
29 | *
30 | * @param islifemode
31 | * 如果为真,则设置各项参数为定位生活场景照片(如百度图片)的参数,否则恢复默认值。
32 | *
33 | */
34 | public void setLifemode(boolean islifemode) {
35 | if (islifemode) {
36 | setGaussianBlurSize(5);
37 | setMorphSizeWidth(9);
38 | setMorphSizeHeight(3);
39 | setVerifyError(0.9f);
40 | setVerifyAspect(4);
41 | setVerifyMin(1);
42 | setVerifyMax(30);
43 | } else {
44 | setGaussianBlurSize(DEFAULT_GAUSSIANBLUR_SIZE);
45 | setMorphSizeWidth(DEFAULT_MORPH_SIZE_WIDTH);
46 | setMorphSizeHeight(DEFAULT_MORPH_SIZE_HEIGHT);
47 | setVerifyError(DEFAULT_ERROR);
48 | setVerifyAspect(DEFAULT_ASPECT);
49 | setVerifyMin(DEFAULT_VERIFY_MIN);
50 | setVerifyMax(DEFAULT_VERIFY_MAX);
51 | }
52 | }
53 |
54 | /**
55 | * 定位车牌图像
56 | *
57 | * @param src
58 | * 原始图像
59 | * @return 一个Mat的向量,存储所有抓取到的图像
60 | */
61 | public Vector plateLocate(Mat src) {
62 | Vector resultVec = new Vector();
63 |
64 | Mat src_blur = new Mat();
65 | Mat src_gray = new Mat();
66 | Mat grad = new Mat();
67 |
68 | int scale = SOBEL_SCALE;
69 | int delta = SOBEL_DELTA;
70 | int ddepth = SOBEL_DDEPTH;
71 |
72 | // 高斯模糊。Size中的数字影响车牌定位的效果。
73 | GaussianBlur(src, src_blur, new Size(gaussianBlurSize, gaussianBlurSize), 0, 0, BORDER_DEFAULT);
74 | if (debug) {
75 | imwrite("tmp/debug_GaussianBlur.jpg", src_blur);
76 | }
77 |
78 | // Convert it to gray 将图像进行灰度化
79 | cvtColor(src_blur, src_gray, CV_RGB2GRAY);
80 | if (debug) {
81 | imwrite("tmp/debug_gray.jpg", src_gray);
82 | }
83 |
84 | // 对图像进行Sobel 运算,得到的是图像的一阶水平方向导数。
85 |
86 | // Generate grad_x and grad_y
87 | Mat grad_x = new Mat();
88 | Mat grad_y = new Mat();
89 | Mat abs_grad_x = new Mat();
90 | Mat abs_grad_y = new Mat();
91 |
92 | Sobel(src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);
93 | convertScaleAbs(grad_x, abs_grad_x);
94 |
95 | Sobel(src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT);
96 | convertScaleAbs(grad_y, abs_grad_y);
97 |
98 | // Total Gradient (approximate)
99 | addWeighted(abs_grad_x, SOBEL_X_WEIGHT, abs_grad_y, SOBEL_Y_WEIGHT, 0, grad);
100 |
101 | if (debug) {
102 | imwrite("tmp/debug_Sobel.jpg", grad);
103 | }
104 |
105 | // 对图像进行二值化。将灰度图像(每个像素点有256 个取值可能)转化为二值图像(每个像素点仅有1 和0 两个取值可能)。
106 |
107 | Mat img_threshold = new Mat();
108 | threshold(grad, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
109 |
110 | if (debug) {
111 | imwrite("tmp/debug_threshold.jpg", img_threshold);
112 | }
113 |
114 | // 使用闭操作。对图像进行闭操作以后,可以看到车牌区域被连接成一个矩形装的区域。
115 |
116 | Mat element = getStructuringElement(MORPH_RECT, new Size(morphSizeWidth, morphSizeHeight));
117 | morphologyEx(img_threshold, img_threshold, MORPH_CLOSE, element);
118 |
119 | if (debug) {
120 | imwrite("tmp/debug_morphology.jpg", img_threshold);
121 | }
122 |
123 | // Find 轮廓 of possibles plates 求轮廓。求出图中所有的轮廓。这个算法会把全图的轮廓都计算出来,因此要进行筛选。
124 |
125 | MatVector contours = new MatVector();
126 | findContours(img_threshold, contours, // a vector of contours
127 | CV_RETR_EXTERNAL, // 提取外部轮廓
128 | CV_CHAIN_APPROX_NONE); // all pixels of each contours
129 |
130 | Mat result = new Mat();
131 | if (debug) {
132 | // Draw red contours on the source image
133 | src.copyTo(result);
134 | drawContours(result, contours, -1, new Scalar(0, 0, 255, 255));
135 | imwrite("tmp/debug_Contours.jpg", result);
136 | }
137 |
138 | // Start to iterate to each contour founded
139 | // 筛选。对轮廓求最小外接矩形,然后验证,不满足条件的淘汰。
140 |
141 | Vector rects = new Vector();
142 |
143 | for (int i = 0; i < contours.size(); ++i) {
144 | RotatedRect mr = minAreaRect(contours.get(i));
145 | if (verifySizes(mr))
146 | rects.add(mr);
147 | }
148 |
149 | int k = 1;
150 | for (int i = 0; i < rects.size(); i++) {
151 | RotatedRect minRect = rects.get(i);
152 | if (verifySizes(minRect)) {
153 |
154 | if (debug) {
155 | Point2f rect_points = new Point2f(4);
156 | minRect.points(rect_points);
157 |
158 | for (int j = 0; j < 4; j++) {
159 | Point pt1 = new Point(rect_points.position(j).asCvPoint2D32f());
160 | Point pt2 = new Point(rect_points.position((j + 1) % 4).asCvPoint2D32f());
161 |
162 | line(result, pt1, pt2, new Scalar(0, 255, 255, 255), 1, 8, 0);
163 | }
164 | }
165 |
166 | // rotated rectangle drawing
167 | // 旋转这部分代码确实可以将某些倾斜的车牌调整正,但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。
168 | // 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试这段代码。
169 |
170 | float r = minRect.size().width() / minRect.size().height();
171 | float angle = minRect.angle();
172 | Size rect_size = new Size((int) minRect.size().width(), (int) minRect.size().height());
173 | if (r < 1) {
174 | angle = 90 + angle;
175 | rect_size = new Size(rect_size.height(), rect_size.width());
176 | }
177 | // 如果抓取的方块旋转超过m_angle角度,则不是车牌,放弃处理
178 | if (angle - this.angle < 0 && angle + this.angle > 0) {
179 | // Create and rotate image
180 | Mat rotmat = getRotationMatrix2D(minRect.center(), angle, 1);
181 | Mat img_rotated = new Mat();
182 | warpAffine(src, img_rotated, rotmat, src.size()); // CV_INTER_CUBIC
183 |
184 | Mat resultMat = showResultMat(img_rotated, rect_size, minRect.center(), k++);
185 | resultVec.add(resultMat);
186 |
187 |
188 |
189 | }
190 | }
191 | }
192 | if (debug) {
193 | imwrite("tmp/debug_result.jpg", result);
194 | }
195 |
196 | return resultVec;
197 | }
198 |
199 | // 设置与读取变量
200 |
201 | public void setGaussianBlurSize(int gaussianBlurSize) {
202 | this.gaussianBlurSize = gaussianBlurSize;
203 | }
204 |
205 | public final int getGaussianBlurSize() {
206 | return this.gaussianBlurSize;
207 | }
208 |
209 | public void setMorphSizeWidth(int morphSizeWidth) {
210 | this.morphSizeWidth = morphSizeWidth;
211 | }
212 |
213 | public final int getMorphSizeWidth() {
214 | return this.morphSizeWidth;
215 | }
216 |
217 | public void setMorphSizeHeight(int morphSizeHeight) {
218 | this.morphSizeHeight = morphSizeHeight;
219 | }
220 |
221 | public final int getMorphSizeHeight() {
222 | return this.morphSizeHeight;
223 | }
224 |
225 | public void setVerifyError(float error) {
226 | this.error = error;
227 | }
228 |
229 | public final float getVerifyError() {
230 | return this.error;
231 | }
232 |
233 | public void setVerifyAspect(float aspect) {
234 | this.aspect = aspect;
235 | }
236 |
237 | public final float getVerifyAspect() {
238 | return this.aspect;
239 | }
240 |
241 | public void setVerifyMin(int verifyMin) {
242 | this.verifyMin = verifyMin;
243 | }
244 |
245 | public void setVerifyMax(int verifyMax) {
246 | this.verifyMax = verifyMax;
247 | }
248 |
249 | public void setJudgeAngle(int angle) {
250 | this.angle = angle;
251 | }
252 |
253 | /**
254 | * 是否开启调试模式
255 | *
256 | * @param debug
257 | */
258 | public void setDebug(boolean debug) {
259 | this.debug = debug;
260 | }
261 |
262 | /**
263 | * 获取调试模式状态
264 | *
265 | * @return
266 | */
267 | public boolean getDebug() {
268 | return debug;
269 | }
270 |
271 | /**
272 | * 对minAreaRect获得的最小外接矩形,用纵横比进行判断
273 | *
274 | * @param mr
275 | * @return
276 | */
277 | private boolean verifySizes(RotatedRect mr) {
278 | float error = this.error;
279 |
280 | // China car plate size: 440mm*140mm,aspect 3.142857
281 | float aspect = this.aspect;
282 | int min = 44 * 14 * verifyMin; // minimum area
283 | int max = 44 * 14 * verifyMax; // maximum area
284 |
285 | // Get only patchs that match to a respect ratio.
286 | float rmin = aspect - aspect * error;
287 | float rmax = aspect + aspect * error;
288 |
289 | int area = (int) (mr.size().height() * mr.size().width());
290 | float r = mr.size().width() / mr.size().height();
291 | if (r < 1)
292 | r = mr.size().height() / mr.size().width();
293 |
294 | return area >= min && area <= max && r >= rmin && r <= rmax;
295 | }
296 |
297 | /**
298 | * 显示最终生成的车牌图像,便于判断是否成功进行了旋转。
299 | *
300 | * @param src
301 | * @param rect_size
302 | * @param center
303 | * @param index
304 | * @return
305 | */
306 | private Mat showResultMat(Mat src, Size rect_size, Point2f center, int index) {
307 | Mat img_crop = new Mat();
308 | getRectSubPix(src, rect_size, center, img_crop);
309 |
310 | if (debug) {
311 | imwrite("tmp/debug_crop_" + index + ".jpg", img_crop);
312 | }
313 |
314 | Mat resultResized = new Mat();
315 | resultResized.create(HEIGHT, WIDTH, TYPE);
316 | resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);
317 | if (debug) {
318 | imwrite("tmp/debug_resize_" + index + ".jpg", resultResized);
319 | }
320 | return resultResized;
321 | }
322 |
323 | // PlateLocate所用常量
324 | public static final int DEFAULT_GAUSSIANBLUR_SIZE = 5;
325 | public static final int SOBEL_SCALE = 1;
326 | public static final int SOBEL_DELTA = 0;
327 | public static final int SOBEL_DDEPTH = CV_16S;
328 | public static final int SOBEL_X_WEIGHT = 1;
329 | public static final int SOBEL_Y_WEIGHT = 0;
330 | public static final int DEFAULT_MORPH_SIZE_WIDTH = 17;
331 | public static final int DEFAULT_MORPH_SIZE_HEIGHT = 3;
332 |
333 | // showResultMat所用常量
334 | public static final int WIDTH = 136;
335 | public static final int HEIGHT = 36;
336 | public static final int TYPE = CV_8UC3;
337 |
338 | // verifySize所用常量
339 | public static final int DEFAULT_VERIFY_MIN = 3;
340 | public static final int DEFAULT_VERIFY_MAX = 20;
341 |
342 | final float DEFAULT_ERROR = 0.6f;
343 | final float DEFAULT_ASPECT = 3.75f;
344 | // 角度判断所用常量
345 | public static final int DEFAULT_ANGLE = 30;
346 |
347 | // 是否开启调试模式常量
348 | public static final boolean DEFAULT_DEBUG = true;
349 |
350 | // 高斯模糊所用变量
351 | protected int gaussianBlurSize = DEFAULT_GAUSSIANBLUR_SIZE;
352 |
353 | // 连接操作所用变量
354 | protected int morphSizeWidth = DEFAULT_MORPH_SIZE_WIDTH;
355 | protected int morphSizeHeight = DEFAULT_MORPH_SIZE_HEIGHT;
356 |
357 | // verifySize所用变量
358 | protected float error = DEFAULT_ERROR;
359 | protected float aspect = DEFAULT_ASPECT;
360 | protected int verifyMin = DEFAULT_VERIFY_MIN;
361 | protected int verifyMax = DEFAULT_VERIFY_MAX;
362 |
363 | // 角度判断所用变量
364 | protected int angle = DEFAULT_ANGLE;
365 |
366 | // 是否开启调试模式,0关闭,非0开启
367 | protected boolean debug = DEFAULT_DEBUG;
368 | }
369 |
--------------------------------------------------------------------------------
/src/org/easypr/core/PlateRecognize.java:
--------------------------------------------------------------------------------
1 | package org.easypr.core;
2 |
3 | import org.bytedeco.javacpp.opencv_core.*;
4 |
5 | import java.util.Vector;
6 |
7 |
8 | /**
9 | * @author Created by fanwenjie
10 | * @author lin.yao
11 | *
12 | */
13 | public class PlateRecognize {
14 |
15 | public int plateRecognize(Mat src, Vector licenseVec) {
16 | //车牌方块集合
17 | Vector plateVec = new Vector();
18 | int resultPD = plateDetect.plateDetect(src, plateVec);
19 | if (resultPD == 0) {
20 | int num = (int) plateVec.size();
21 | for (int j = 0; j < num; j++) {
22 | Mat plate = plateVec.get(j);
23 | //获取车牌颜色
24 | String plateType = charsRecognise.getPlateType(plate);
25 | //获取车牌号
26 | String plateIdentify = charsRecognise.charsRecognise(plate);
27 | String license = plateType + ":" + plateIdentify;
28 | licenseVec.add(license);
29 | System.out.println("车牌识别结果: " + plateType +" "+ plateIdentify);
30 | }
31 | }
32 | return resultPD;
33 | }
34 |
35 | /**
36 | * 设置是否开启生活模式
37 | * @param lifemode
38 | */
39 | public void setLifemode(boolean lifemode) {
40 | plateDetect.setPDLifemode(lifemode);
41 | }
42 |
43 | /**
44 | * 是否开启调试模式
45 | * @param debug
46 | */
47 | public void setDebug(boolean debug) {
48 | plateDetect.setPDDebug(debug);
49 | charsRecognise.setCRDebug(debug);
50 | }
51 |
52 | private PlateDetect plateDetect = new PlateDetect();
53 | private CharsRecognise charsRecognise = new CharsRecognise();
54 | }
55 |
--------------------------------------------------------------------------------
/src/org/easypr/core/SVMCallback.java:
--------------------------------------------------------------------------------
1 | package org.easypr.core;
2 |
3 | import org.bytedeco.javacpp.opencv_core.Mat;
4 |
5 |
6 | /**
7 | * @author Created by fanwenjie
8 | * @author lin.yao
9 | *
10 | */
11 | public interface SVMCallback {
12 |
13 | /***
14 | * EasyPR的getFeatures回调函数,本函数是生成直方图均衡特征的回调函数
15 | *
16 | * @param image
17 | * @return
18 | */
19 | public abstract Mat getHisteqFeatures(final Mat image);
20 |
21 | /**
22 | * EasyPR的getFeatures回调函数, 本函数是获取垂直和水平的直方图图值
23 | *
24 | * @param image
25 | * @return
26 | */
27 | public abstract Mat getHistogramFeatures(final Mat image);
28 |
29 | /**
30 | * 本函数是获取SITF特征子的回调函数
31 | *
32 | * @param image
33 | * @return
34 | */
35 | public abstract Mat getSIFTFeatures(final Mat image);
36 |
37 | /**
38 | * 本函数是获取HOG特征子的回调函数
39 | *
40 | * @param image
41 | * @return
42 | */
43 | public abstract Mat getHOGFeatures(final Mat image);
44 | }
45 |
--------------------------------------------------------------------------------
/src/org/easypr/test/EasyPrTest.java:
--------------------------------------------------------------------------------
1 | package org.easypr.test;
2 |
3 |
4 | import static org.bytedeco.javacpp.opencv_highgui.imread;
5 | import static org.easypr.core.CoreFunc.getPlateType;
6 | import static org.easypr.core.CoreFunc.projectedHistogram;
7 | import static org.easypr.core.CoreFunc.showImage;
8 |
9 | import java.util.Vector;
10 |
11 | import org.bytedeco.javacpp.opencv_core.IplImage;
12 | import org.bytedeco.javacpp.opencv_core.Mat;
13 | import org.easypr.core.CharsIdentify;
14 | import org.easypr.core.CharsRecognise;
15 | import org.easypr.core.CoreFunc;
16 | import org.easypr.core.PlateDetect;
17 | import org.easypr.core.PlateLocate;
18 | import org.junit.Test;
19 |
20 | /**
21 | * @author lin.yao
22 | *
23 | */
24 | public class EasyPrTest {
25 |
26 | @Test
27 | public static void testPlateRecognise(Mat matImage) {
28 | //String imgPath = "res/image/test_image/test.jpg";
29 | String imgPath = "res/image/test_image/plate_recognize.jpg";
30 |
31 | Mat src = imread(imgPath);
32 | PlateDetect plateDetect = new PlateDetect();
33 | plateDetect.setPDLifemode(true);
34 | Vector matVector = new Vector();
35 | if (0 == plateDetect.plateDetect(matImage, matVector)) {
36 | CharsRecognise cr = new CharsRecognise();
37 |
38 | for (int i = 0; i < matVector.size(); ++i) {
39 | String result = cr.charsRecognise(matVector.get(i));
40 | System.out.println("Chars Recognised: " + result);
41 | }
42 | }
43 | }
44 |
45 | @Test
46 | public void testPlateDetect() {
47 | String imgPath = "res/image/test_image/test.jpg";
48 |
49 | Mat src = imread(imgPath);
50 | PlateDetect plateDetect = new PlateDetect();
51 | plateDetect.setPDLifemode(true);
52 | Vector matVector = new Vector();
53 | if (0 == plateDetect.plateDetect(src, matVector)) {
54 | for (int i = 0; i < matVector.size(); ++i) {
55 | showImage("Plate Detected", matVector.get(i));
56 | }
57 | }
58 | }
59 |
60 | @Test
61 | public void testPlateLocate() {
62 | String imgPath = "res/image/test_image/test.jpg";
63 |
64 | Mat src = imread(imgPath);
65 |
66 | PlateLocate plate = new PlateLocate();
67 | plate.setDebug(true);
68 | plate.setLifemode(true);
69 |
70 | Vector resultVec = plate.plateLocate(src);
71 |
72 | int num = resultVec.size();
73 | for (int j = 0; j < num; j++) {
74 | showImage("Plate Located " + j, resultVec.get(j));
75 | }
76 |
77 | return;
78 | }
79 |
80 | @Test
81 | public void testCharsRecognise() {
82 | String imgPath = "res/image/test_image/chars_recognise_huAGH092.jpg";
83 |
84 | Mat src = imread(imgPath);
85 | CharsRecognise cr = new CharsRecognise();
86 | cr.setCRDebug(true);
87 | String result = cr.charsRecognise(src);
88 | System.out.println("Chars Recognised: " + result);
89 | }
90 |
91 | @Test
92 | public void testColorDetect() {
93 | String imgPath = "res/image/test_image/core_func_yellow.jpg";
94 |
95 | Mat src = imread(imgPath);
96 |
97 | CoreFunc.Color color = getPlateType(src, true);
98 | System.out.println("Color Deteted: " + color);
99 | }
100 |
101 | @Test
102 | public void testProjectedHistogram() {
103 | String imgPath = "res/image/test_image/chars_identify_E.jpg";
104 |
105 | Mat src = imread(imgPath);
106 | projectedHistogram(src, CoreFunc.Direction.HORIZONTAL);
107 | }
108 |
109 | @Test
110 | public void testCharsIdentify() {
111 | String imgPath = "res/image/test_image/chars_identify_E.jpg";
112 |
113 | Mat src = imread(imgPath);
114 | CharsIdentify charsIdentify = new CharsIdentify();
115 | String result = charsIdentify.charsIdentify(src, false, true);
116 | System.out.println(result);
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/src/org/easypr/test/Test.java:
--------------------------------------------------------------------------------
1 | package org.easypr.test;
2 |
3 | import org.easypr.core.CharsRecognise;
4 | import org.easypr.core.PlateDetect;
5 |
6 | import java.util.Vector;
7 |
8 | import static org.bytedeco.javacpp.opencv_core.*;
9 | import static org.bytedeco.javacpp.opencv_highgui.*;
10 |
11 | /*
12 | * Created by fanwenjie
13 | * @version 1.1
14 | */
15 |
16 | public class Test {
17 |
18 | public static void plateDetect(){
19 | String imgPath = "res/image/baidu_image/test2.jpg";
20 | System.out.println("Plate Detect Test");
21 | Mat src = imread(imgPath);
22 | PlateDetect plateDetect = new PlateDetect();
23 | plateDetect.setPDLifemode(true);
24 | Vector matVector = new Vector();
25 | plateDetect.plateDetect(src, matVector);
26 | if(0==plateDetect.plateDetect(src,matVector)){
27 | long num = matVector.size();
28 | for(int i=0;i trainingLabels = new Vector();
115 | String path = "res/train/data/chars_recognise_ann/chars2/";
116 |
117 | for (int i = 0; i < numCharacter; i++) {
118 | System.out.println("Character: " + strCharacters[i]);
119 | String str = path + '/' + strCharacters[i];
120 | Vector files = new Vector();
121 | Util.getFiles(str, files);
122 |
123 | int size = (int) files.size();
124 | for (int j = 0; j < size; j++) {
125 | System.out.println(files.get(j));
126 | Mat img = imread(files.get(j), 0);
127 | Mat f5 = features(img, 5);
128 | Mat f10 = features(img, 10);
129 | Mat f15 = features(img, 15);
130 | Mat f20 = features(img, 20);
131 |
132 | trainingDataf5.push_back(f5);
133 | trainingDataf10.push_back(f10);
134 | trainingDataf15.push_back(f15);
135 | trainingDataf20.push_back(f20);
136 | trainingLabels.add(i); // 每一幅字符图片所对应的字符类别索引下标
137 | }
138 | }
139 |
140 | path = "res/train/data/chars_recognise_ann/charsChinese";
141 |
142 | for (int i = 0; i < strChinese.length; i++) {
143 | System.out.println("Character: " + strChinese[i]);
144 | String str = path + '/' + strChinese[i];
145 | Vector files = new Vector();
146 | Util.getFiles(str, files);
147 |
148 | int size = (int) files.size();
149 | for (int j = 0; j < size; j++) {
150 | System.out.println(files.get(j));
151 | Mat img = imread(files.get(j), 0);
152 | Mat f5 = features(img, 5);
153 | Mat f10 = features(img, 10);
154 | Mat f15 = features(img, 15);
155 | Mat f20 = features(img, 20);
156 |
157 | trainingDataf5.push_back(f5);
158 | trainingDataf10.push_back(f10);
159 | trainingDataf15.push_back(f15);
160 | trainingDataf20.push_back(f20);
161 | trainingLabels.add(i + numCharacter);
162 | }
163 | }
164 |
165 | trainingDataf5.convertTo(trainingDataf5, CV_32FC1);
166 | trainingDataf10.convertTo(trainingDataf10, CV_32FC1);
167 | trainingDataf15.convertTo(trainingDataf15, CV_32FC1);
168 | trainingDataf20.convertTo(trainingDataf20, CV_32FC1);
169 | int[] labels = new int[trainingLabels.size()];
170 | for (int i = 0; i < labels.length; ++i)
171 | labels[i] = trainingLabels.get(i).intValue();
172 | new Mat(labels).copyTo(classes);
173 |
174 | FileStorage fs = new FileStorage("res/train/ann_data.xml", FileStorage.WRITE);
175 | fs.writeObj("TrainingDataF5", trainingDataf5.data());
176 | fs.writeObj("TrainingDataF10", trainingDataf10.data());
177 | fs.writeObj("TrainingDataF15", trainingDataf15.data());
178 | fs.writeObj("TrainingDataF20", trainingDataf20.data());
179 | fs.writeObj("classes", classes.data());
180 | fs.release();
181 |
182 | System.out.println("End saveTrainData");
183 | return 0;
184 | }
185 |
186 | public void saveModel(int _predictsize, int _neurons) {
187 | FileStorage fs = new FileStorage("res/train/ann_data.xml", FileStorage.READ);
188 | String training = "TrainingDataF" + _predictsize;
189 | Mat TrainingData = new Mat(fs.get(training).readObj());
190 | Mat Classes = new Mat(fs.get("classes"));
191 |
192 | // train the Ann
193 | System.out.println("Begin to saveModelChar predictSize:" + Integer.valueOf(_predictsize).toString());
194 | System.out.println(" neurons:" + Integer.valueOf(_neurons).toString());
195 |
196 | long start = getTickCount();
197 | annTrain(TrainingData, Classes, _neurons);
198 | long end = getTickCount();
199 | System.out.println("GetTickCount:" + Long.valueOf((end - start) / 1000).toString());
200 |
201 | System.out.println("End the saveModelChar");
202 |
203 | String model_name = "res/train/ann.xml";
204 |
205 | // if(1)
206 | // {
207 | // String str =
208 | // String.format("ann_prd:%d\tneu:%d",_predictsize,_neurons);
209 | // model_name = str;
210 | // }
211 |
212 | CvFileStorage fsto = CvFileStorage.open(model_name, CvMemStorage.create(), CV_STORAGE_WRITE);
213 | ann.write(fsto, "ann");
214 | }
215 |
216 | public int annMain() {
217 | System.out.println("To be begin.");
218 |
219 | saveTrainData();
220 |
221 | // 可根据需要训练不同的predictSize或者neurons的ANN模型
222 | // for (int i = 2; i <= 2; i ++)
223 | // {
224 | // int size = i * 5;
225 | // for (int j = 5; j <= 10; j++)
226 | // {
227 | // int neurons = j * 10;
228 | // saveModel(size, neurons);
229 | // }
230 | // }
231 |
232 | // 这里演示只训练model文件夹下的ann.xml,此模型是一个predictSize=10,neurons=40的ANN模型。
233 | // 根据机器的不同,训练时间不一样,但一般需要10分钟左右,所以慢慢等一会吧。
234 | saveModel(10, 40);
235 |
236 | System.out.println("To be end.");
237 | return 0;
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/src/org/easypr/train/SVMTrain.java:
--------------------------------------------------------------------------------
1 | package org.easypr.train;
2 |
3 |
4 | import org.easypr.core.Features;
5 | import org.easypr.util.Convert;
6 | import org.easypr.core.SVMCallback;
7 |
8 | import static org.bytedeco.javacpp.opencv_core.*;
9 | import static org.bytedeco.javacpp.opencv_highgui.*;
10 | import static org.bytedeco.javacpp.opencv_ml.*;
11 |
12 | import org.easypr.util.Util;
13 |
14 | import java.util.*;
15 |
16 | /*
17 | * Created by fanwenjie
18 | * @version 1.1
19 | */
20 | public class SVMTrain {
21 |
22 | private SVMCallback callback = new Features();
23 | private static final String hasPlate = "HasPlate";
24 | private static final String noPlate = "NoPlate";
25 |
26 | public SVMTrain(SVMCallback callback){
27 | this.callback = callback;
28 | }
29 |
30 | public SVMTrain(){
31 |
32 | this.learn2HasPlate();
33 | this.learn2NoPlate();
34 | }
35 |
36 |
37 | private void learn2Plate(float bound, final String name) {
38 | final String filePath = "res/train/data/plate_detect_svm/learn/" + name;
39 | Vector files = new Vector();
40 | ////获取该路径下的所有文件
41 | Util.getFiles(filePath, files);
42 | int size = files.size();
43 | if (0 == size) {
44 | System.out.println("File not found in " + filePath);
45 | return;
46 | }
47 | Collections.shuffle(files, new Random(new Date().getTime()));
48 | ////随机选取70%作为训练数据,30%作为测试数据
49 | int boundry = (int) (bound * size);
50 |
51 | Util.recreateDir("res/train/data/plate_detect_svm/train/" + name);
52 | Util.recreateDir("res/train/data/plate_detect_svm/test/" + name);
53 |
54 | System.out.println("Save " + name + " train!");
55 | for (int i = 0; i < boundry; i++) {
56 | System.out.println(files.get(i));
57 | Mat img = imread(files.get(i));
58 | String str = "res/train/data/plate_detect_svm/train/" + name + "/" + name + "_" + Integer.valueOf(i).toString() + ".jpg";
59 | imwrite(str, img);
60 | }
61 |
62 | System.out.println("Save " + name + " test!");
63 | for (int i = boundry; i < size; i++) {
64 | System.out.println(files.get(i));
65 | Mat img = imread(files.get(i));
66 | String str = "res/train/data/plate_detect_svm/test/" + name + "/" + name + "_" + Integer.valueOf(i).toString() + ".jpg";
67 | imwrite(str, img);
68 | }
69 | }
70 |
71 | private void getPlateTrain(Mat trainingImages, Vector trainingLabels, final String name) {
72 | int label = 1;
73 | final String filePath = "res/train/data/plate_detect_svm/train/" + name;
74 | Vector files = new Vector();
75 |
76 | ////获取该路径下的所有文件
77 | Util.getFiles(filePath, files);
78 |
79 | int size = files.size();
80 | if (0 == size) {
81 | System.out.println("File not found in " + filePath);
82 | return;
83 | }
84 | System.out.println("get " + name + " train!");
85 | for (int i = 0; i < size; i++) {
86 | //System.out.println(files[i].c_str()).toString());
87 | Mat img = imread(files.get(i));
88 |
89 | //调用回调函数决定特征
90 | Mat features = this.callback.getHisteqFeatures(img);
91 | features = features.reshape(0,0);
92 | trainingImages.push_back(features);
93 | trainingLabels.add(label);
94 | }
95 | }
96 |
97 | private void getPlateTest(MatVector testingImages,Vector testingLabels,final String name){
98 | int label = 1;
99 | final String filePath = "res/train/data/plate_detect_svm/test/"+name;
100 | Vector files = new Vector();
101 | Util.getFiles(filePath, files);
102 |
103 | int size = files.size();
104 | if (0 == size) {
105 | System.out.println("File not found in " + filePath);
106 | return;
107 | }
108 | System.out.println("get "+name+" test!");
109 | for (int i = 0; i < size; i++)
110 | {
111 | Mat img = imread(files.get(i));
112 | testingImages.put(img);
113 | testingLabels.add(label);
114 | }
115 | }
116 |
117 | public void learn2HasPlate() {
118 | learn2HasPlate(0.7f);
119 | }
120 |
121 | public void learn2HasPlate(float bound) {
122 | learn2Plate(bound, hasPlate);
123 | }
124 |
125 | public void learn2NoPlate() {
126 | learn2NoPlate(0.7f);
127 | }
128 |
129 | public void learn2NoPlate(float bound) {
130 | learn2Plate(bound, noPlate);
131 | }
132 |
133 |
134 | public void getNoPlateTrain(Mat trainingImages, Vector trainingLabels) {
135 | getPlateTrain(trainingImages, trainingLabels, noPlate);
136 | }
137 |
138 | public void getHasPlateTrain(Mat trainingImages, Vector trainingLabels) {
139 | getPlateTrain(trainingImages, trainingLabels, hasPlate);
140 | }
141 |
142 |
143 | public void getHasPlateTest(MatVector testingImages,Vector testingLabels)
144 | {
145 | getPlateTest(testingImages,testingLabels,hasPlate);
146 | }
147 |
148 | public void getNoPlateTest(MatVector testingImages,Vector testingLabels)
149 | {
150 | getPlateTest(testingImages,testingLabels,noPlate);
151 | }
152 |
153 |
154 |
155 | //! 测试SVM的准确率,回归率以及FScore
156 | public void getAccuracy(Mat testingclasses_preditc, Mat testingclasses_real)
157 | {
158 | int channels = testingclasses_preditc.channels();
159 | System.out.println("channels: "+Integer.valueOf(channels).toString());
160 | int nRows = testingclasses_preditc.rows();
161 | System.out.println("nRows: "+Integer.valueOf(nRows).toString());
162 | int nCols = testingclasses_preditc.cols() * channels;
163 | System.out.println("nCols: "+Integer.valueOf(nCols).toString());
164 | int channels_real = testingclasses_real.channels();
165 | System.out.println("channels_real: "+Integer.valueOf(channels_real).toString());
166 | int nRows_real = testingclasses_real.rows();
167 | System.out.println("nRows_real: " + Integer.valueOf(nRows_real).toString());
168 | int nCols_real = testingclasses_real.cols() * channels;
169 | System.out.println("nCols_real: "+Integer.valueOf(nCols_real).toString());
170 |
171 | double count_all = 0;
172 | double ptrue_rtrue = 0;
173 | double ptrue_rfalse = 0;
174 | double pfalse_rtrue = 0;
175 | double pfalse_rfalse = 0;
176 |
177 | for (int i = 0; i < nRows; i++)
178 | {
179 |
180 | final float predict = Convert.toFloat(testingclasses_preditc.ptr(i));
181 | final float real = Convert.toFloat(testingclasses_real.ptr(i));
182 |
183 | count_all ++;
184 |
185 | //System.out.println("predict:" << predict).toString());
186 | //System.out.println("real:" << real).toString());
187 |
188 | if (predict == 1.0 && real == 1.0)
189 | ptrue_rtrue ++;
190 | if (predict == 1.0 && real == 0)
191 | ptrue_rfalse ++;
192 | if (predict == 0 && real == 1.0)
193 | pfalse_rtrue ++;
194 | if (predict == 0 && real == 0)
195 | pfalse_rfalse ++;
196 | }
197 |
198 | System.out.println("count_all: "+Double.valueOf(count_all).toString());
199 | System.out.println("ptrue_rtrue: "+Double.valueOf(ptrue_rtrue).toString());
200 | System.out.println("ptrue_rfalse: "+Double.valueOf(ptrue_rfalse).toString());
201 | System.out.println("pfalse_rtrue: "+Double.valueOf(pfalse_rtrue).toString());
202 | System.out.println("pfalse_rfalse: "+Double.valueOf(pfalse_rfalse).toString());
203 |
204 | double precise = 0;
205 | if (ptrue_rtrue + ptrue_rfalse != 0)
206 | {
207 | precise = ptrue_rtrue/(ptrue_rtrue + ptrue_rfalse);
208 | System.out.println("precise: "+Double.valueOf(precise).toString());
209 | }
210 | else
211 | {
212 | System.out.println("precise: NA");
213 | }
214 |
215 | double recall = 0;
216 | if (ptrue_rtrue + pfalse_rtrue != 0)
217 | {
218 | recall = ptrue_rtrue/(ptrue_rtrue + pfalse_rtrue);
219 | System.out.println("recall: "+Double.valueOf(recall).toString());
220 | }
221 | else
222 | {
223 | System.out.println("recall: NA");
224 | }
225 |
226 | if (precise + recall != 0)
227 | {
228 | double F = (precise * recall)/(precise + recall);
229 | System.out.println("F: "+Double.valueOf(F).toString());
230 | }
231 | else
232 | {
233 | System.out.println("F: NA");
234 | }
235 | }
236 |
237 |
238 | public int svmTrain(boolean dividePrepared, boolean trainPrepared)
239 | {
240 |
241 | Mat classes = new Mat();
242 | Mat trainingData = new Mat();
243 |
244 | Mat trainingImages = new Mat();
245 | Vector trainingLabels = new Vector();
246 |
247 |
248 | if (!dividePrepared)
249 | {
250 | //分割learn里的数据到train和test里
251 | System.out.println("Divide learn to train and test");
252 | learn2HasPlate();
253 | learn2NoPlate();
254 | }
255 |
256 | //将训练数据加载入内存
257 | if (!trainPrepared)
258 | {
259 | System.out.print("Begin to get train data to memory");
260 | getHasPlateTrain(trainingImages, trainingLabels);
261 | getNoPlateTrain(trainingImages, trainingLabels);
262 |
263 |
264 | trainingImages.copyTo(trainingData);
265 | trainingData.convertTo(trainingData, CV_32FC1);
266 |
267 | int []labels = new int[trainingLabels.size()];
268 | for(int i=0;i testingLabels_real = new Vector();
276 |
277 | //将测试数据加载入内存
278 | System.out.println("Begin to get test data to memory");
279 | getHasPlateTest(testingImages, testingLabels_real);
280 | getNoPlateTest(testingImages, testingLabels_real);
281 |
282 | CvSVM svm = new CvSVM();
283 | if (!trainPrepared && !classes.empty() && !trainingData.empty())
284 | {
285 | CvSVMParams SVM_params = new CvSVMParams(CvSVM.C_SVC,CvSVM.RBF,0.1,1,0.1,1,0.1,0.1,
286 | new CvMat(),new CvTermCriteria().type(CV_TERMCRIT_ITER).max_iter(100000).epsilon(0.0001));
287 |
288 | //Train SVM
289 | System.out.println("Begin to generate svm");
290 |
291 | try {
292 | //CvSVM svm(trainingData, classes, Mat(), Mat(), SVM_params);
293 | svm.train_auto(trainingData, classes, new Mat(), new Mat(), SVM_params, 10,
294 | CvSVM.get_default_grid(CvSVM.C),
295 | CvSVM.get_default_grid(CvSVM.GAMMA),
296 | CvSVM.get_default_grid(CvSVM.P),
297 | CvSVM.get_default_grid(CvSVM.NU),
298 | CvSVM.get_default_grid(CvSVM.COEF),
299 | CvSVM.get_default_grid(CvSVM.DEGREE),
300 | true);
301 | } catch (Exception err) {
302 | System.out.println(err.getMessage());
303 | }
304 |
305 | System.out.println("Svm generate done!");
306 |
307 | CvFileStorage fsTo = CvFileStorage.open("res/rain/svm.xml", CvMemStorage.create(),CV_STORAGE_WRITE);
308 | svm.write(fsTo, "svm");
309 | }
310 | else
311 | {
312 | try {
313 | String path = "res/train/svm.xml";
314 | svm.load(path, "svm");
315 | } catch (Exception err) {
316 | System.out.println(err.getMessage());
317 | return 0; //next predict requires svm
318 | }
319 | }
320 |
321 | System.out.println("Begin to predict");
322 |
323 | double count_all = 0;
324 | double ptrue_rtrue = 0;
325 | double ptrue_rfalse = 0;
326 | double pfalse_rtrue = 0;
327 | double pfalse_rfalse = 0;
328 |
329 | int size = (int)testingImages.size();
330 | for (int i = 0; i < size; i++)
331 | {
332 | //System.out.println(files[i].c_str());
333 | Mat p = testingImages.get(i);
334 |
335 | //调用回调函数决定特征
336 | Mat features = callback.getHistogramFeatures(p);
337 | features = features.reshape(1, 1);
338 | features.convertTo(features, CV_32FC1);
339 |
340 | int predict = (int)svm.predict(features);
341 | int real = testingLabels_real.get(i);
342 |
343 | if (predict == 1 && real == 1)
344 | ptrue_rtrue ++;
345 | if (predict == 1 && real == 0)
346 | ptrue_rfalse ++;
347 | if (predict == 0 && real == 1)
348 | pfalse_rtrue ++;
349 | if (predict == 0 && real == 0)
350 | pfalse_rfalse ++;
351 | }
352 |
353 | count_all = size;
354 |
355 | System.out.println("Get the Accuracy!");
356 |
357 | System.out.println("count_all: "+Double.valueOf(count_all).toString());
358 | System.out.println("ptrue_rtrue: "+Double.valueOf(ptrue_rtrue).toString());
359 | System.out.println("ptrue_rfalse: "+Double.valueOf(ptrue_rfalse).toString());
360 | System.out.println("pfalse_rtrue: "+Double.valueOf(pfalse_rtrue).toString());
361 | System.out.println("pfalse_rfalse: "+Double.valueOf(pfalse_rfalse).toString());
362 |
363 | double precise = 0;
364 | if (ptrue_rtrue + ptrue_rfalse != 0)
365 | {
366 | precise = ptrue_rtrue / (ptrue_rtrue + ptrue_rfalse);
367 | System.out.println("precise: "+Double.valueOf(precise).toString());
368 | }
369 | else
370 | System.out.println("precise: NA");
371 |
372 | double recall = 0;
373 | if (ptrue_rtrue + pfalse_rtrue != 0)
374 | {
375 | recall = ptrue_rtrue / (ptrue_rtrue + pfalse_rtrue);
376 | System.out.println("recall: "+Double.valueOf(recall).toString());
377 | }
378 | else
379 | System.out.println("recall: NA");
380 |
381 | double Fsocre = 0;
382 | if (precise + recall != 0)
383 | {
384 | Fsocre = 2 * (precise * recall) / (precise + recall);
385 | System.out.println("Fsocre: "+Double.valueOf(Fsocre).toString());
386 | }
387 | else
388 | System.out.println("Fsocre: NA");
389 | return 0;
390 | }
391 | }
392 |
--------------------------------------------------------------------------------
/src/org/easypr/util/Convert.java:
--------------------------------------------------------------------------------
1 | package org.easypr.util;
2 |
3 | import org.bytedeco.javacpp.BytePointer;
4 |
5 | /**
6 | * There are 3 kinds of convert functions:
7 | * 1. [float|double|int|long] to[Float|Double|Int|Long](BytePointer pointer)
8 | * 2. byte[] getBytes([float|double|int|long] value)
9 | * 3. [float|double|int|long] to[Float|Double|Int|Long](byte[] value)
10 | *
11 | * @author lin.yao
12 | *
13 | */
14 | public class Convert {
15 |
16 | public static float toFloat(BytePointer pointer) {
17 | byte[] buffer = new byte[4];
18 | pointer.get(buffer);
19 | return toFloat(buffer);
20 | }
21 |
22 | public static double toDouble(BytePointer pointer) {
23 | byte[] buffer = new byte[8];
24 | pointer.get(buffer);
25 | return toDouble(buffer);
26 | }
27 |
28 | public static int toInt(BytePointer pointer) {
29 | byte[] buffer = new byte[4];
30 | pointer.get(buffer);
31 | return toInt(buffer);
32 | }
33 |
34 | public static long toLong(BytePointer pointer) {
35 | byte[] buffer = new byte[8];
36 | pointer.get(buffer);
37 | return toLong(buffer);
38 | }
39 |
40 | public static byte[] getBytes(float value) {
41 | return getBytes(Float.floatToIntBits(value));
42 | }
43 |
44 | public static byte[] getBytes(double value) {
45 | return getBytes(Double.doubleToLongBits(value));
46 | }
47 |
48 | public static byte[] getBytes(int value) {
49 | final int length = 4;
50 | byte[] buffer = new byte[length];
51 | for (int i = 0; i < length; ++i)
52 | buffer[i] = (byte) ((value >> (i * 8)) & 0xFF);
53 | return buffer;
54 | }
55 |
56 | public static byte[] getBytes(long value) {
57 | final int length = 8;
58 | byte[] buffer = new byte[length];
59 | for (int i = 0; i < length; ++i)
60 | buffer[i] = (byte) ((value >> (i * 8)) & 0xFF);
61 | return buffer;
62 | }
63 |
64 | public static int toInt(byte[] value) {
65 | final int length = 4;
66 | int n = 0;
67 | for (int i = 0; i < length; ++i)
68 | n += (value[i] & 0xFF) << (i * 8);
69 | return n;
70 | }
71 |
72 | public static long toLong(byte[] value) {
73 | final int length = 8;
74 | long n = 0;
75 | for (int i = 0; i < length; ++i)
76 | n += ((long) (value[i] & 0xFF)) << (i * 8);
77 | return n;
78 | }
79 |
80 | public static double toDouble(byte[] value) {
81 | return Double.longBitsToDouble(toLong(value));
82 | }
83 |
84 | public static float toFloat(byte[] value) {
85 | return Float.intBitsToFloat(toInt(value));
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/org/easypr/util/Util.java:
--------------------------------------------------------------------------------
1 | package org.easypr.util;
2 |
3 | import java.io.File;
4 | import java.util.Vector;
5 |
6 |
7 | /**
8 | * @author lin.yao
9 | *
10 | */
11 | public class Util {
12 |
13 | /**
14 | * get all files under the directory path
15 | *
16 | * @param path
17 | * @param files
18 | */
19 | public static void getFiles(final String path, Vector files) {
20 | getFiles(new File(path), files);
21 | }
22 |
23 | /**
24 | * delete and create a new directory with the same name
25 | *
26 | * @param dir
27 | */
28 | public static void recreateDir(final String dir) {
29 | new File(dir).delete();
30 | new File(dir).mkdir();
31 | }
32 |
33 | private static void getFiles(final File dir, Vector files) {
34 | File[] filelist = dir.listFiles();
35 | for (File file : filelist) {
36 | if (file.isDirectory()) {
37 | getFiles(file, files);
38 | } else {
39 | files.add(file.getAbsolutePath());
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------