功能介绍
本篇是介绍屏幕搜索功能,屏幕搜索主要是指在屏幕指定区域内,搜索特定的图形或特定的颜色。开始的时候走了弯路,我是直接遍历屏幕像素点,然后取色比较的,这样做效率特别低,10X10的像素区域,就要1秒多,不得已还用了多线程。后来找到一篇文章,先截屏然后在生成的图片对象中查找,速度快很多 。
代码
- 屏幕区域截图工具类
package com.analog.tools;
import java.awt.AWTException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
public class CutImageTool {
/**
* 截取指定左上角坐标,右下角坐标的屏幕区域图像
* @param topLeft
* @param bottomRight
* @return
* @throws AWTException
*/
public static BufferedImage getScreeImage(int topLeftX, int topLeftY, int bottomRightX, int bottomRightY) throws AWTException {
int width = bottomRightX - topLeftX;
int height = bottomRightY - topLeftY;
Rectangle rectangle = new Rectangle(topLeftX, topLeftY, width, height);
return CommonUtil.getRobot().createScreenCapture(rectangle);
}
/**
* 截取指定左上角坐标,右下角坐标的屏幕区域图像
* @param topLeft
* @param bottomRight
* @return
* @throws AWTException
*/
public static BufferedImage getScreeImage(Point topLeft, Point bottomRight) throws AWTException {
int X = topLeft.x;
int Y = topLeft.y;
int width = bottomRight.x - topLeft.x;
int height = bottomRight.y - topLeft.y;
Rectangle rectangle = new Rectangle(X, Y, width, height);
return CommonUtil.getRobot().createScreenCapture(rectangle);
}
/**
* 截取指定屏幕区域图像
* @param topLeft
* @param bottomRight
* @return
* @throws AWTException
*/
public static BufferedImage getScreeImage(Rectangle rectangle) throws AWTException {
return CommonUtil.getRobot().createScreenCapture(rectangle);
}
/**
* 截取全屏幕图像
* @return
* @throws AWTException
*/
public static BufferedImage getFullScreeImage() throws AWTException {
Rectangle rectangle = new Rectangle(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
return CommonUtil.getRobot().createScreenCapture(rectangle);
}
}
- 图形搜索工具类
搜索区域的特点是,坐标固定(通常是全屏幕,或游戏全窗口),这个需要实时抓取不需要保存;而待搜索的特定图案的特使,是图案是不变,坐标位置不确定(这个位置就是我们要找的),因此可以保存成文件,用的时候再读取内容。
实时截取大图,小图,然后在大图中搜索小图没有问题。但是把小图保存成jpg文件,读取小图内容,再在大图中搜索就失败了。我猜测可能是保存时,颜色信息失真了。因此保存小图时,我是直接保存颜色的rgb值的信息到文件里.
搜索工具类代码
package com.analog.tools;
import java.awt.AWTException;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import com.analog.games.mir2.entity.GameScreen;
/**
* 图像匹配
* @author Jiyh
*
*/
public class ImageMatchTools {
/**
* 全屏幕查找指定区域的图像
* @param topLeft
* @param bottomRight
* @return
* @throws AWTException
*/
public static int[] findImageInFullScreen(Point topLeft, Point bottomRight) throws AWTException {
BufferedImage smallImage = CutImageTool.getScreeImage(topLeft, bottomRight);
BufferedImage screencapture = CutImageTool.getFullScreeImage();
return findImage(screencapture, smallImage);
}
/**
* 全屏幕区域查找指定文件代表的图像
* @param bigImage
* @param smallImageFileName
* @return
* @throws AWTException
*/
public static int[] findImage( String smallImageFileName) throws AWTException {
BufferedImage screenImage = CutImageTool.getFullScreeImage();
return findImage(screenImage, ImageMatchTools.readImageRGB2Object(smallImageFileName));
}
/**
* 在指定屏幕区域查找指定文件代表的图像
* @param bigImage
* @param smallImageFileName
* @return
*/
public static int[] findImage(BufferedImage creenImage, String smallImageFileName) {
return findImage(creenImage, ImageMatchTools.readImageRGB2Object(smallImageFileName));
}
/**
* 在游戏屏幕区域查找指定文件代表的图像
* @param gameScreenTopLeft
* @param gameScreenBottomRight
* @param smallImageFileName
* @return
* @throws AWTException
*/
public static int[] findImageInGameScreen( String smallImageFileName) throws AWTException {
BufferedImage gameScreenImage = CutImageTool.getScreeImage(GameScreen.topLeft, GameScreen.bottomRight);
return findImage(gameScreenImage, ImageMatchTools.readImageRGB2Object(smallImageFileName));
}
/**
* 在指定屏幕区域查找指定二位数组代表的图像
* @param bigImage
* @param smallRgbArray
* @return
*/
public static int[] findImage(BufferedImage bigImage, int[][] smallRgbArray) {
int bigWidth = bigImage.getWidth();
int bigHeight = bigImage.getHeight();
int smallWidth = smallRgbArray.length;
int smallHeight = smallRgbArray[0].length;
int[][] bigData = getImageRGB(bigImage);
int[][] smallData = smallRgbArray;
int[] target = { -1, -1 };
int yEnd = bigHeight - smallHeight;
int xEnd = bigWidth - smallWidth;
for (int y = 0; y < yEnd; y++) {
for (int x = 0; x < xEnd; x++) {
// 对关键点进行先期匹配,降低运算复杂度。如果关键点本身就不匹配,就没必要再去匹配小图的每一个像素点
if (bigData[x][y] == smallData[0][0] // 左上角
&& bigData[x + smallWidth - 1][y]
== smallData[smallWidth - 1][0] // 右上角
&& bigData[x][y + smallHeight - 1] == smallData[0][smallHeight - 1] // 左下角
&& bigData[x + smallWidth - 1][y + smallHeight - 1] == smallData[smallWidth - 1][smallHeight
- 1] // 右下角
&& bigData[x + smallWidth / 2][y + smallHeight / 2] == smallData[smallWidth / 2][smallHeight
/ 2]) {
// 进行全像素匹配
boolean isMatched = checkAllMatch(x, y, smallHeight, smallWidth, bigData, smallData);
if (isMatched) {
System.out.println("像素点X" + x + " : Y" + y + ",对应的值为:" + bigData[x][y]);
// 获取小图的中心位置的点
int centerX = x + smallWidth/2;
int centerY = y + smallHeight/2;
target[0] = centerX;
target[1] = centerY;
return target;
}
}
}
}
return target;
}
/**
* 在指定屏幕区域查找指定图像
* @param bigImage
* @param smallImage
* @return
*/
public static int[] findImage(BufferedImage bigImage, BufferedImage smallImage) {
int bigWidth = bigImage.getWidth();
int bigHeight = bigImage.getHeight();
int smallWidth = smallImage.getWidth();
int smallHeight = smallImage.getHeight();
int[][] bigData = getImageRGB(bigImage);
int[][] smallData = getImageRGB(smallImage);
int[] target = { -1, -1 };
int yEnd = bigHeight - smallHeight;
int xEnd = bigWidth - smallWidth;
for (int y = 0; y < yEnd; y++) {
for (int x = 0; x < xEnd; x++) {
// 对关键点进行先期匹配,降低运算复杂度。如果关键点本身就不匹配,就没必要再去匹配小图的每一个像素点
if (bigData[x][y] == smallData[0][0] // 左上角
&& bigData[x + smallWidth - 1][y] == smallData[smallWidth - 1][0] // 右上角
&& bigData[x][y + smallHeight - 1] == smallData[0][smallHeight - 1] // 左下角
&& bigData[x + smallWidth - 1][y + smallHeight - 1] == smallData[smallWidth - 1][smallHeight
- 1] // 右下角
&& bigData[x + smallWidth / 2][y + smallHeight / 2] == smallData[smallWidth / 2][smallHeight
/ 2]) {
// 进行全像素匹配
boolean isMatched = checkAllMatch(x, y, smallHeight, smallWidth, bigData, smallData);
if (isMatched) {
System.out.println("像素点X" + x + " : Y" + y + ",对应的值为:" + bigData[x][y]);
// 获取小图的中心位置的点
int centerX = x + smallWidth/2;
int centerY = y + smallHeight/2;
target[0] = centerX;
target[1] = centerY;
return target;
}
}
}
}
return target;
}
private static boolean checkAllMatch(int x, int y, int smallHeight, int smallWidth, int[][] bigData,
int[][] smallData) {
boolean isMatched = true;
for (int smallY = 0; smallY < smallHeight; smallY++) {
for (int smallX = 0; smallX < smallWidth; smallX++) {
// 如果发现有一个像素点,两者的值不一样,则认为不相等,如果不相等,则没必要继续比较其它点.
if (bigData[x + smallX][y + smallY] != smallData[smallX][smallY]) {
isMatched = false;
return isMatched;
}
}
}
return isMatched;
}
public static int[][] getImageRGB(BufferedImage bfImage) {
int width = bfImage.getWidth();
int height = bfImage.getHeight();
int[][] result = new int[width][height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 对某个像素点的RGB编码并存入数据库
result[x][y] = bfImage.getRGB(x, y) & 0xFFFFFF;
// 单独获取每一个像素点的Red,Green,和Blue的值。
// int r = (bfImage.getRGB(x, y) & 0xFF0000) >> 16;
// int g = (bfImage.getRGB(x, y) & 0xFF00) >> 8;
// int b = bfImage.getRGB(x, y) & 0xFF;
}
}
return result;
}
public static int[][] readImageRGB(String fileName) {
List<int[]> rgbList = new ArrayList<int[]>();
TxtFileReader tfr = null;
try {
tfr = new TxtFileReader(fileName);
String line = null;
while ((line = tfr.readLine()) != null){
String[] rgbStringArray = line.split(",");
int[] rfgArray = new int[rgbStringArray.length];
for (int i = 0; i < rgbStringArray.length; i++) {
rfgArray[i] = Integer.parseInt(rgbStringArray[i]);
}
rgbList.add(rfgArray);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(tfr != null){
tfr.close();
}
}
int[][] rgbArray = new int[rgbList.size()][];
for (int i = 0; i < rgbArray.length; i++) {
rgbArray[i] = rgbList.get(i);
}
return rgbArray;
}
public static int[][] readImageRGB2Object(String filePath) {
File file = new File(filePath);
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(file);
ois = new ObjectInputStream(fis);
Object data = ois.readObject();
return (int[][])data;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
}
- 保存屏幕截图
这里说明一下,保存rgb颜色信息文件,为什么使用Object。原因有点尴尬:直接遍历数据组,一行行写入文件后,在读取出来,数据结构和原来的结构不一样。调整几次也没成功。因此就直接写数组对象本身到文件里了,反正写成数字也是看不懂的。
//图片保存路径->自定义
String baseDir = "E:\\games\\keyMacro\\mir2\\";
//rgb颜色信息文件名(搜索时使用的文件)
String fileName = "waigua.clr";
//jpg文件名,用于查看截图是否正确
String jpgFileName = "waigua.clr.jpg";
//微信截图工具获取的目标图形的坐标信息
int topleftX = 550;
int topleftY = 270;
int bottomrightX = 560;
int bottomrightY = 280;
//获取图形对象
BufferedImage bfImage = CutImageTool.getScreeImage(topleftX, topleftY, bottomrightX, bottomrightY);
//获取图像的颜色信息数组
//转换为搜索时需要的数据结构
int[][] rgbArray = ImageMatchTools.getImageRGB(bfImage);
写rgb颜色信息文件
File file = new File(baseDir + fileName);
FileOutputStream fos = new FileOutputStream(file);//新建一个文件输出流
ObjectOutputStream oos = new ObjectOutputStream(fos);//在文件输出流上套一个对象输出流
oos.writeObject(rgbArray);
oos.flush();
oos.close();
//写jpg图形文件
File jpgFile = new File(baseDir + jpgFileName);
ImageIO.write(bfImage, "jpg", jpgFile);
小结一下
到这里,Java版按键精灵的基础功能都OK了,后面的内容是我在自己的游戏中的实践,仅供参考。。