戳这里→康康你手机号在过多少网站注册过!!!
友情推荐:新一代安全短信
谷歌已经宣布退出图形验证码服务,为何国内各种奇葩验证方式层出不穷,安全性到底如何?
《腾讯防水墙滑动拼图验证码》
《百度旋转图片验证码》
《网易易盾滑动拼图验证码》
《顶象区域面积点选验证码》
《顶象滑动拼图验证码》
《极验滑动拼图验证码》
《使用深度学习来破解 captcha 验证码》
根据场景来看,我们需要根据图片中分隔好的区域找到面积最大的一块来点击它。
那么我们把它拆分成以下几个步骤:
- 检测出图中标记的点
- 将检测出来的点连成线
- 根据线分割出的区域计算各区域面积,并得到最大面积
- 在该区域面积中选取一个坐标点作为结果
第一个问题,怎么检测出图片中被标记出来的点?
这里使用哈里斯角点检测,这里采用OpenCV中的cornerHarris()来实现。
参考下面两篇文章,感兴趣的话可以阅读一下:
/**
* 哈里斯角点检测
* @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());
// 转灰度图像
Mat gray = new Mat();
Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY);
// 角点发现
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) {
// 越接近于角点数值越大
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();
}
}
那标记点的检测完成了。
如何连线就比较简单了,这里我们只需要在绘制角点的时候将浸染范围设置大一点就好了,这里设置为5即可。
Imgproc.circle(mat, new Point(j, i), 5, new Scalar(0, 255, 0));
这里根据深度优先搜索的原理,划分不同区域最终选出最大的一块面积;
深度优先搜索大家不会的话就可以参考这篇文章:
基本算法——深度优先搜索(DFS)和广度优先搜索(BFS)
这里直接搜索了所有区域。将占像素量最多的区域显示了出来,效果如图:
/**根据线分割出的区域计算各区域面积,并得到最大面积
* @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,得到一个代替图的二维数组
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;
}
}
}
//深度优先搜索找出最大区域
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;
}
//改变最大区域颜色
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();
}
}
/**
* 深度优先搜索
* @param matrix 图信息数组
* @param n 标记数
* @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;
}
这里我们都已经找到面积最大区域了,就随意取一个点就好了
将上面代码中的
//改变最大区域颜色
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());
}
}
}
改为下面的代码即可
//标记选取到的点
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;
}
}