前言
我們又來破解驗證碼啦,今天上場的是–頂象面積驗證碼
根據(jù)場景來看,我們需要根據(jù)圖片中分隔好的區(qū)域找到面積最大的一塊來點擊它。
那么我們把它拆分成以下幾個步驟:
檢測出圖中標(biāo)記的點將檢測出來的點連成線根據(jù)線分割出的區(qū)域計算各區(qū)域面積,并得到最大面積在該區(qū)域面積中選取一個坐標(biāo)點作為結(jié)果
一、檢測出圖中標(biāo)記的點
第一個問題,怎么檢測出圖片中被標(biāo)記出來的點?
這里使用哈里斯角點檢測,這里采用OpenCV中的cornerHarris()來實現(xiàn)。
參考下面兩篇文章,感興趣的話可以閱讀一下:
Harris角點檢測原理詳解圖像特征之Harris角點檢測
效果如下圖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/** * 哈里斯角點檢測 * @param img 原圖地址 * @param img2 新圖地址 */ public void getHarris(String img,String img2) { System.load(dllPath); File bFile = new File(img); try { Mat mat = Imgcodecs.imread(bFile.getPath()); // 轉(zhuǎn)灰度圖像 Mat gray = new Mat(); Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY); // 角點發(fā)現(xiàn) Mat harris = new Mat(); Imgproc.cornerHarris(gray, harris, 2 , 3 , 0.04 ); // 繪制角點 float [] floats = new float [harris.cols()]; for ( int i = 0 ; i < harris.rows(); i++) { harris.get(i, 0 , floats); for ( int j = 0 ; j < floats.length; j++) { if (floats[j] > 0.0001 ) { // 越接近于角點數(shù)值越大 System.out.println(floats[j]); Imgproc.circle(mat, new Point(j, i), 1 , new Scalar( 0 , 255 , 0 )); } } } Imgcodecs.imwrite(img2, mat); } catch (Throwable e) { e.printStackTrace(); } } |
那標(biāo)記點的檢測完成了。
二、將檢測出來的點連成線
如何連線就比較簡單了,這里我們只需要在繪制角點的時候?qū)⒔痉秶O(shè)置大一點就好了,這里設(shè)置為5即可。
1
|
Imgproc.circle(mat, new Point(j, i), 5 , new Scalar( 0 , 255 , 0 )); |
下面是效果圖
連線做到這樣的效果就可以了。
三、根據(jù)線分割出的區(qū)域計算各區(qū)域面積,并得到最大面積
這里根據(jù)深度優(yōu)先搜索的原理,劃分不同區(qū)域最終選出最大的一塊面積;
深度優(yōu)先搜索大家不會的話就可以參考這篇文章:
基本算法——深度優(yōu)先搜索(DFS)和廣度優(yōu)先搜索(BFS)
這里直接搜索了所有區(qū)域。將占像素量最多的區(qū)域顯示了出來,效果如圖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
/**根據(jù)線分割出的區(qū)域計算各區(qū)域面積,并得到最大面積 * @param oldimg 原圖 * @param newimg 繪制角點后的圖 */ */ public void getMatrix(String oldimg,String newimg) { File ofile = new File(oldimg); File nfile = new File(newimg); try { BufferedImage oimage = ImageIO.read(ofile); BufferedImage nimage = ImageIO.read(nfile); int matrix[][] = new int [nimage.getWidth()][nimage.getHeight()]; int rank = 0 ; int maxRank = 0 ; int count = 0 ; int maxCount = 0 ; //將檢測并高亮部分置1,其余部分置0,得到一個代替圖的二維數(shù)組 for ( int w = 0 ; w < nimage.getWidth(); w++) { for ( int h = 0 ; h < nimage.getHeight(); h++) { int [] bgRgb = new int [ 3 ]; bgRgb[ 0 ] = (nimage.getRGB(w, h) & 0xff0000 ) >> 16 ; bgRgb[ 1 ] = (nimage.getRGB(w, h) & 0xff00 ) >> 8 ; bgRgb[ 2 ] = (nimage.getRGB(w, h) & 0xff ); if (!(bgRgb[ 0 ] <= 70 && bgRgb[ 1 ] >= 180 && bgRgb[ 2 ] <= 70 )) { matrix[w][h] = 0 ; } else { matrix[w][h] = - 1 ; } } } //深度優(yōu)先搜索找出最大區(qū)域 while ( true ) { int n = 0 ; for ( int i = 0 ; i < matrix.length; i++) { for ( int j = 0 ; j < matrix[ 0 ].length; j++) { if (matrix[i][j] == 0 ) { n++; rank++; count = dfs(matrix, rank); if (count > maxCount) { maxCount = count; maxRank = rank; } } } } if (n == 0 ) break ; } //改變最大區(qū)域顏色 for ( int j = 0 ; j < matrix[ 0 ].length; j++) { for ( int i = 0 ; i < matrix.length; i++) { if (matrix[i][j] == maxRank){ nimage.setRGB(i, j, new Color( 0 , 0 , 255 ).getRGB()); } } } ImageIO.write(image, "png" , new File(img)); } catch (IOException e) { e.printStackTrace(); } } /** * 深度優(yōu)先搜索 * @param matrix 圖信息數(shù)組 * @param n 標(biāo)記數(shù) * @return */ public int dfs( int matrix[][], int rank) { int count = 0 ; int w = - 1 ; int h = - 1 ; for ( int i = 0 ; i < matrix.length; i++) { for ( int j = 0 ; j < matrix[ 0 ].length; j++) { if (matrix[i][j] == 0 ) { w = i; h = j; break ; } } if (w != - 1 ) { break ; } } Stack<JSONObject> stack = new Stack<JSONObject>(); while (matrix[w][h] == 0 || h == stack.peek().getIntValue( "h" ) && w == stack.peek().getIntValue( "w" )) { JSONObject json = new JSONObject(); json.put( "w" , w); json.put( "h" , h); stack.push(json); matrix[w][h] = rank; count++; if (h + 1 < matrix[ 0 ].length) { if (matrix[w][h + 1 ] == 0 ) { h = h + 1 ; continue ; } } if (w + 1 < matrix.length) { if (matrix[w + 1 ][h] == 0 ) { w = w + 1 ; continue ; } } if (h - 1 >= 0 ) { if (matrix[w][h - 1 ] == 0 ) { h = h - 1 ; continue ; } } if (w - 1 >= 0 ) { if (matrix[w - 1 ][h] == 0 ) { w = w - 1 ; continue ; } } stack.pop(); if (!stack.empty()) { if (h == stack.peek().getIntValue( "h" ) && w == stack.peek().getIntValue( "w" )) { stack.pop(); } } if (!stack.empty()) { w = stack.peek().getIntValue( "w" ); h = stack.peek().getIntValue( "h" ); } else { break ; } } return count; } |
四、 在該區(qū)域面積中選取一個坐標(biāo)點作為結(jié)果
這里我們都已經(jīng)找到面積最大區(qū)域了,就隨意取一個點就好了
將上面代碼中的
1
2
3
4
5
6
7
8
|
//改變最大區(qū)域顏色 for ( int j = 0 ; j < matrix[ 0 ].length; j++) { for ( int i = 0 ; i < matrix.length; i++) { if (matrix[i][j] == maxRank){ nimage.setRGB(i, j, new Color( 0 , 0 , 255 ).getRGB()); } } } |
改為下面的代碼即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//標(biāo)記選取到的點 boolean flag = false ; for ( int j = 0 ; j < matrix[ 0 ].length; j++) { for ( int i = 0 ; i < matrix.length; i++) { if (matrix[i][j] == maxRank) { oimage.setRGB(i, j, new Color( 255 , 0 , 0 ).getRGB()); System.out.println( "w=" + i + "|h=" + j); flag = true ; break ; } } if (flag) { break ; } } |
結(jié)果展示:
本文思路參考:https://blog.csdn.net/aaronjny/article/details/110245896
到此這篇關(guān)于使用java + OpenCV破解頂象面積驗證碼的示例的文章就介紹到這了,更多相關(guān)java頂象面積驗證碼內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/weixin_49701447/article/details/110627934