Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package opencv; import demo.getTempPath; import static java.lang.System.out; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.opencv.core.Core; import org.opencv.core.CvException; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.MatOfPoint2f; import org.opencv.core.Point; import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.core.TermCriteria; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; public class CaptchaDetection { private static final String SAMPLES_PATH = getTempPath.getTEMP_PATH() + "number_samples\\"; private static final Map<Integer, List<Mat>> sampleMat = new TreeMap<>(); private static final rect_cmp_by_center rectSort = new rect_cmp_by_center(); private static boolean INIT_LIBARY = false; private static boolean INIT_SAMPLE = false; static { try { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); INIT_LIBARY = true; for (int i = 0; i < 20; i++) { Mat sample = Imgcodecs.imread(SAMPLES_PATH + i + ".png", Imgcodecs.IMREAD_GRAYSCALE); List<Mat> rotateList = new ArrayList<>(); for (int rota = -90; rota <= 90; rota += 5) { Mat rotateImg = sample.clone(); rotateImage(rotateImg, rotateImg, rota); rotateList.add(rotateImg); } sampleMat.put(i, rotateList); } INIT_SAMPLE = true; //out.println("? ! "); } catch (UnsatisfiedLinkError | CvException ex) { out.println("Initialization failed in case :" + ex.toString()); } } public static boolean isINIT_LIBARY() { return INIT_LIBARY; } public static boolean isINIT_SAMPLE() { return INIT_SAMPLE; } public static String goDetect(String filename) throws CvException { final Mat src = Imgcodecs.imread(filename); Mat src_gray = thres_rgb(src); Mat noise = k_means_spilter(src); Mat line = check_is_line(src_gray); delete_target(src_gray, noise, line); delete_point(src_gray); Mat src_resize = new Mat(); Imgproc.resize(src_gray, src_resize, new Size(src_gray.cols() * 2, src_gray.rows() * 2)); List<Mat> num_rio = find_number(src_resize); String answer = dect_number(num_rio); return answer; } private static Mat thres_rgb(Mat src) { Mat gray = Mat.zeros(src.size(), CvType.CV_8UC1); // , ? int thres = 150; double gamma = 2.5; for (int row = 0; row < src.rows(); row++) { for (int col = 0; col < src.cols(); col++) { double[] s_data = src.get(row, col); byte[] s_buff = new byte[3]; byte[] g_buff = new byte[1]; double color_sum = s_data[0] + s_data[1] + s_data[2]; if (color_sum / 3 > thres) { for (int channel = 0; channel < 3; channel++) s_buff[channel] = (byte) 255; g_buff[0] = 0; } else { // gamma for (int channel = 0; channel < 3; channel++) { double tmp = s_data[channel]; tmp = Math.pow(tmp / 255, gamma) * 255; if (tmp < 0) s_buff[channel] = 0; else if (tmp > 255) s_buff[channel] = (byte) 255; else s_buff[channel] = (byte) tmp; } g_buff[0] = (byte) 255; } src.put(row, col, s_buff); gray.put(row, col, g_buff); } } return gray; } private static Mat k_means_spilter(Mat src) { Mat dst = Mat.zeros(src.size(), CvType.CV_8UC1); int width = src.cols(); int height = src.rows(); int dims = src.channels(); // int clusterCount = 3; Mat points = new Mat(width * height, dims, CvType.CV_32F, new Scalar(0)); Mat centers = new Mat(clusterCount, dims, CvType.CV_32F); Mat labels = new Mat(width * height, 1, CvType.CV_32S); // points for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { int index = row * width + col; double[] s_data = src.get(row, col); for (int channel = 0; channel < 3; channel++) { float[] f_buff = new float[1]; f_buff[0] = (float) s_data[channel]; points.put(index, channel, f_buff); } } } // knn ? TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.MAX_ITER, 10, 0.1); Core.kmeans(points, clusterCount, labels, criteria, 3, Core.KMEANS_PP_CENTERS, centers); // ??? label index Map<Integer, Integer> tmp = new TreeMap<>(); for (int i = 0; i < clusterCount; i++) { int sum = 0; for (int j = 0; j < dims; j++) { sum += centers.get(i, j)[0]; } while (tmp.containsKey(sum)) sum++; tmp.put(sum, i); } int count = 0; int[] label_order = new int[clusterCount]; for (Map.Entry<Integer, Integer> iter : tmp.entrySet()) { label_order[count++] = iter.getValue(); } for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { int index = row * width + col; int label = (int) labels.get(index, 0)[0]; if (label == label_order[1]) { byte[] d_buff = new byte[1]; d_buff[0] = (byte) 255; dst.put(row, col, d_buff); } } } return dst; } private static Mat check_is_line(Mat src) { Mat dst = Mat.zeros(src.size(), CvType.CV_8UC1); int min_length = 3; // ? for (int row = 0; row < src.rows(); row++) { for (int col = 0; col < src.cols(); col++) { if (src.get(row, col)[0] == 0) continue; // ?? boolean left_black = false, right_black = false; if (col == 0 || src.get(row, col - 1)[0] == 0) left_black = true; if (col == src.cols() - 1 || src.get(row, col + 1)[0] == 0) right_black = true; if (!left_black || !right_black) continue; // int length = col_length(src, row, col); if (length > min_length) { byte[] d_buff = new byte[1]; d_buff[0] = (byte) 255; dst.put(row, col, d_buff); } } } // ? for (int row = 0; row < src.rows(); row++) { for (int col = 0; col < src.cols(); col++) { if (src.get(row, col)[0] == 0) continue; // ? boolean up_black = false, down_black = false; if (row == 0 || src.get(row - 1, col)[0] == 0) up_black = true; if (row == src.rows() - 1 || src.get(row + 1, col)[0] == 0) down_black = true; if (!up_black || !down_black) continue; // int length = row_length(src, row, col); if (length > min_length) { byte[] d_buff = new byte[1]; d_buff[0] = (byte) 255; dst.put(row, col, d_buff); } } } return dst; } private static int col_length(Mat src, int dst_row, int dst_col) { int length = 0; for (int row = dst_row; row >= 0; row--) { if (src.get(row, dst_col)[0] == 255) length++; else break; } for (int row = dst_row + 1; row < src.rows(); row++) { if (src.get(row, dst_col)[0] == 255) length++; else break; } //out.println(length); return length; } private static int row_length(Mat src, int dst_row, int dst_col) { int length = 0; for (int col = dst_col; col >= 0; col--) { if (src.get(dst_row, col)[0] == 255) length++; else break; } for (int col = dst_col + 1; col < src.cols(); col++) { if (src.get(dst_row, col)[0] == 255) length++; else break; } //out.println(length); return length; } /*** * src dst_0 dst_1 * @param src * @param dst_0 * @param dst_1 */ private static void delete_target(Mat src, Mat dst_0, Mat dst_1) { for (int row = 0; row < src.rows(); row++) { for (int col = 0; col < src.cols(); col++) { if (dst_0.get(row, col)[0] == 255 && dst_1.get(row, col)[0] == 255) { byte[] d_buff = new byte[1]; d_buff[0] = (byte) 0; src.put(row, col, d_buff); } } } } /*** * * @param src */ private static void delete_point(Mat src) { for (int row = 0; row < src.rows(); row++) { for (int col = 0; col < src.cols(); col++) { if (src.get(row, col)[0] == 255) { int count = 0; if (row == 0 || src.get(row - 1, col)[0] == 0) count++; if (row == src.rows() - 1 || src.get(row + 1, col)[0] == 0) count++; if (col == 0 || src.get(row, col - 1)[0] == 0) count++; if (col == src.cols() - 1 || src.get(row, col + 1)[0] == 0) count++; if (count == 4) { byte[] d_buff = new byte[1]; d_buff[0] = (byte) 0; src.put(row, col, d_buff); } } } } } /*** * ??, ROI * @param src * @return */ private static List<Mat> find_number(Mat src) { Mat src_tmp = src.clone(); // Imgproc.dilate(src_tmp, src_tmp, new Mat()); // ? Mat canny_edge = new Mat(); Imgproc.blur(src_tmp, src_tmp, new Size(3, 3)); Imgproc.Canny(src_tmp, canny_edge, 50, 150, 3, false); // List<MatOfPoint> contours = new ArrayList<>(); Imgproc.findContours(canny_edge, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); List<Rect> boundRect = new ArrayList<>(); // ??, ?? for (int i = 0; i < contours.size(); i++) { MatOfPoint2f tmp_mp2f_1 = new MatOfPoint2f(); MatOfPoint2f tmp_mp2f_2 = new MatOfPoint2f(); contours.get(i).convertTo(tmp_mp2f_1, CvType.CV_32FC2); Imgproc.approxPolyDP(tmp_mp2f_1, tmp_mp2f_2, 3, true); tmp_mp2f_2.convertTo(contours.get(i), CvType.CV_32S); Rect rect = Imgproc.boundingRect(contours.get(i)); //if (rect.area() > 300) //out.println("h : " + rect.height + ", w : " + rect.width + ", aera : " + rect.area()); if (rect.height >= 21 && rect.width >= 21 && rect.area() >= 700) boundRect.add(rect); } // ?? for (Rect rect : boundRect) { Scalar color = new Scalar(128); Imgproc.rectangle(src_tmp, rect.tl(), rect.br(), color, 2, 8, 0); } // ??? Collections.sort(boundRect, rectSort); List<Mat> numRoi = new ArrayList<>(); for (Rect rect : boundRect) numRoi.add(src.submat(rect)); //for (Mat roi : numRoi) //showResult(roi, "roi"); return numRoi; } /*** * ? * @param src * @param dst * @param degree */ private static void rotateImage(Mat src, Mat dst, int degree) { Point center = new Point(src.cols() / 2.0 + 0.5, src.rows() / 2.0 + 0.5); Mat M = Imgproc.getRotationMatrix2D(center, degree, 1.0); Imgproc.warpAffine(src, dst, M, src.size()); } /*** * ? * @param src * @return */ private static String dect_number(List<Mat> src) { String answer = ""; for (Mat numRoi : src) { Mat zoomNum = new Mat(numRoi.rows() * 2, numRoi.cols() * 2, CvType.CV_8UC1, new Scalar(0)); numRoi.copyTo( zoomNum.submat(new Rect(numRoi.cols() / 2, numRoi.rows() / 2, numRoi.cols(), numRoi.rows()))); double matchMin = Double.MAX_VALUE; int matchSample = 0; for (Map.Entry<Integer, List<Mat>> iter : sampleMat.entrySet()) { for (Mat sample : iter.getValue()) { int result_cols = zoomNum.cols() - sample.cols() + 1; int result_rows = zoomNum.rows() - sample.rows() + 1; Mat resultImg = new Mat(result_rows, result_cols, CvType.CV_32FC1); Imgproc.matchTemplate(zoomNum, sample, resultImg, Imgproc.TM_SQDIFF); Core.MinMaxLocResult mmr = Core.minMaxLoc(resultImg); if (matchMin > mmr.minVal) { matchMin = mmr.minVal; matchSample = iter.getKey(); } } } answer += matchSample / 2; //out.println("NumRio\tmatch sample : " + matchSample + "\tmatch value : " + matchMin); } //out.println("Answer is : " + answer); return answer; } /*** * Rect? */ private static class rect_cmp_by_center implements Comparator<Rect> { @Override public int compare(Rect a, Rect b) { int center_a = a.x + a.width / 2; int center_b = b.x + b.width / 2; return center_a - center_b; } } }