【验证码识别】顶象面积验证码

前言
在这里插入图片描述

我们又来破解验证码啦,今天上场的是–顶象面积验证码
在这里插入图片描述

根据场景来看,我们需要根据图片中分隔好的区域找到面积最大的一块来点击它。

那么我们把它拆分成以下几个步骤:

  1. 检测出图中标记的点
  2. 将检测出来的点连成线
  3. 根据线分割出的区域计算各区域面积,并得到最大面积
  4. 在该区域面积中选取一个坐标点作为结果

一、检测出图中标记的点

第一个问题,怎么检测出图片中被标记出来的点?

这里使用哈里斯角点检测,这里采用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;
	}
}

结果展示:

在这里插入图片描述
本文思路参考:https://blog.csdn.net/aaronjny/article/details/110245896
在这里插入图片描述

作者:香芋味的猫丶
戳这里→康康你手机号在过多少网站注册过!!!
友情推荐:新一代安全短信

猜你喜欢

转载自blog.csdn.net/weixin_44549063/article/details/112193019