忽然心血来潮,想写一个辅助工具,让朋友们在“大家来找茬”之类的游戏中可以少费一些眼睛。
在Java方面我是新手,在折腾了一段时间后,终于还是写出了一个基本可用的测试版程序。详细的使用方法和使用效果,可以参见这个博客http://blog.sina.com.cn/s/blog_9245c9e0010136by.html
这个小程序也可以通过这种方法进行下载——CSDN的资源中搜索“自己用Java写的图像比较器”

这里我想和大家分享一下编写过程中遇到的问题,同时也把源代码公布于此,朋友们可以批评指正。

思路:

类似于“大家来找茬”的游戏,可以用这样的思路来破解:
1.截取第一幅图A
2.截图第二幅图B
3.通过逐个像素比较,将差异部分显示出来

但是实际编程时,发现了问题——你很难确保两次截图时的大小位置完全一致!

开始我还想着通过算法来判断,后来想到了一个好解法——机器是死的,人是活的啊。所以步骤2可以调整为“将第一幅截图显示在窗口中,通过人工移动窗口,将两幅图叠加”

源码分享:

最终的程序由三个类构成

myScreenCapture:实现截图功能,并把截图的数据传给PicCheckFrame
PicCheckFrame:创建一个新窗口,用户可以手工移动该窗口,实现两幅图片的叠加

PicCheckPanel:实现步骤3中的算法,显示两幅图片的差异

/*** @author LiuCC* 本类实现了截屏功能* 即在屏幕上选择一块区域(如大家来找茬中的A图片),然后将选定的区域交给另一个类去处理比较**/
public class myScreenCapture extends JFrame{private JButton screenCaptureButton, exitButton;/*** 程序入口* @param args*/public static void main(String[] args) {// TODO Auto-generated method stubnew myScreenCapture();  //非常简单,直接new即可}/** 构造函数* 一个截图按钮,一个退出按钮*/public myScreenCapture(){super("Picture Checker by LiuCC");initWindow(); //把这个窗体构建函数拆开拆开以便于修改和复用       }private void initWindow(){setLayout(new FlowLayout()); //若无此句,只会显示最后一个加入的按钮screenCaptureButton = new JButton("截图");add(screenCaptureButton);//设置截图的处理函数screenCaptureButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {// 1.利用Toolkit获得全屏幕大小// 2.利用Robot将截屏结果放入BufferedImage// 3.建立一个全屏幕大小的Frame,里面放置一个自定义的Panel以做为鼠标截图的显示区域try{Toolkit tk = Toolkit.getDefaultToolkit();Dimension screenSize = tk.getScreenSize();Rectangle screenRectangle = new Rectangle(0,0,screenSize.width,screenSize.height);Robot myRobot = new Robot();BufferedImage screenBufferedImage = myRobot.createScreenCapture(screenRectangle);JFrame screenFrame = new JFrame();screenFrame.getContentPane().add(new scrCapturePanel(screenFrame, screenBufferedImage, screenSize.width, screenSize.height));screenFrame.setUndecorated(true); //如此设置,可以让用户感觉不到该Frame的存在screenFrame.setAlwaysOnTop(true);//要有如下两句Frame才会显示screenFrame.setVisible(true);screenFrame.setSize(screenSize);}catch(Exception robotException){robotException.printStackTrace();}}});exitButton = new JButton("退出");add(exitButton);exitButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubSystem.exit(0);}});//有了以下两句,窗体才能显示//setSize(220,80);pack();setVisible(true);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}/*** 建立一个与屏幕一致的Panel,主要是为了显示个性化的鼠标,以及通过鼠标拉出选择框* @author LiuCC**/private class scrCapturePanel extends JPanel implements MouseListener, MouseMotionListener{private JFrame parentFrame;private int width, height;private BufferedImage screenImage;private BufferedImage selectImage;private int startX, startY, endX, endY; //在一次选择过程中涉及到的坐标private Rectangle selectRectangle = new Rectangle(0,0,0,0);private int tempX, tempY;private Cursor specialCursor;/*** 构造函数* @param* 参数依次为:父窗口; 在Panel中显示的画面; 宽,高*/public scrCapturePanel(JFrame parentFrame, BufferedImage showedImage, int width, int height){this.parentFrame = parentFrame;this.screenImage = showedImage;this.width = width;this.height = height;//设置一个特别的鼠标,以标识截图状态Image cursorImage = Toolkit.getDefaultToolkit().createImage(this.getClass().getResource("/image/icon.png"));specialCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0,0), "special icon");setCursor(specialCursor);//关联鼠标动作addMouseListener(this);addMouseMotionListener(this);}/*** JPanel绘制的关键函数*/public void paintComponent(Graphics g){g.drawImage(screenImage, 0, 0, width, height, 0, 0, width, height, this);//以红框标识鼠标圈定的范围g.setColor(Color.RED);g.drawLine(startX,startY,endX,startY);g.drawLine(startX,endY,endX,endY);g.drawLine(startX,startY,startX,endY);g.drawLine(endX,startY,endX,endY);//记录选定区域,考虑了反向拖拽画框的情况int x=startX<endX?startX:endX;int y=startY<endY?startY:endY;selectRectangle = new Rectangle(x,y,Math.abs(startX-endX),Math.abs(startY-endY));    }@Overridepublic void mousePressed(MouseEvent e) {// TODO Auto-generated method stubtempX = e.getX();tempY = e.getY();}@Overridepublic void mouseReleased(MouseEvent e) {// TODO Auto-generated method stub//Do Nothing!                        }@Overridepublic void mouseDragged(MouseEvent e) {// TODO Auto-generated method stubstartX=tempX;startY=tempY;endX=e.getX();endY=e.getY();repaint(); //重绘图像,以显示动态效果}@Overridepublic void mouseMoved(MouseEvent e) {if(selectRectangle.contains(e.getPoint())){setCursor(new Cursor(Cursor.MOVE_CURSOR));   //在选定区域内外,采用不同的鼠标样式以示区别}else{setCursor(specialCursor);}}@Overridepublic void mouseClicked(MouseEvent e) {if(2==e.getClickCount()){//在选定区域内双击表示选定该区域if(selectRectangle.contains(e.getPoint())){selectImage = screenImage.getSubimage(selectRectangle.x, selectRectangle.y, selectRectangle.width, selectRectangle.height);parentFrame.dispose();//供测试用,生成一个图片,考察效果try {ImageIO.write(selectImage, "jpg", new File("./test1.jpg"));} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}//接下来启动对比图片所需的Framenew PicCheckFrame(selectImage);}else{//在选定区域外双击,则重新选择startX=0;startY=0;endX=0;endY=0;selectRectangle=new Rectangle(0,0,0,0);repaint();}}}@Overridepublic void mouseEntered(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseExited(MouseEvent e) {// TODO Auto-generated method stub}           }
}

第二个类

/*** 建立一个JFrame的子类,包含两个区域——图片显示区域和一个退出按钮* @author LiuCC* */
public class PicCheckFrame extends JFrame{private int relativeX, relativeY;  //表示鼠标按下时,该点与图像界面原点的相对位置private int absoluteX, absoluteY;  //鼠标拖拽时,鼠标所在绝对位置private int setX, setY;               //拖动时,鼠标相对于图像界面原点的位置private boolean mousePressedNow=false;  //true表示鼠标左键按下private PicCheckPanel pCkPanel;private JButton exitButton;/*** 构造函数* @param get*/public PicCheckFrame(BufferedImage capturedImage){pCkPanel = new PicCheckPanel(capturedImage);setUndecorated(true);    //这样设置可以免去调整标题栏的麻烦//我至今不知道如果加入了标题栏,在之后的函数中如何处理像素点相对位置//如果有朋友搞懂了,可以留言告诉我setLayout(new BorderLayout());add(pCkPanel, BorderLayout.CENTER);exitButton = new JButton("Exit");add(exitButton, BorderLayout.SOUTH);exitButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent arg0) {// TODO Auto-generated method stubSystem.exit(0);}});addMouseListener(new mouseFunction());addMouseMotionListener(new mouseMotionFunction());setSize(capturedImage.getWidth(), capturedImage.getHeight());setVisible(true);}/*** * @author LiuCC**/private class mouseFunction extends MouseAdapter{@Overridepublic void mousePressed(MouseEvent e){if(1==e.getClickCount()){mousePressedNow=true;relativeX = e.getX();relativeY = e.getY();  //e带来的是,鼠标对于当前Component的相对位置}}@Overridepublic void mouseReleased(MouseEvent e){mousePressedNow=false;}}private class mouseMotionFunction extends MouseMotionAdapter{@Overridepublic void mouseDragged(MouseEvent e){if(true==mousePressedNow){//说明此时在拖动窗口int tmpX = PicCheckFrame.this.getLocationOnScreen().x;int tmpY = PicCheckFrame.this.getLocationOnScreen().y;absoluteX = tmpX + e.getX();absoluteY = tmpY + e.getY();//如果SetLocation时用absoluteX,会让鼠标回到Component的原点,这样用户体验不好,所以还应该做如下修正setX = absoluteX - relativeX;setY = absoluteY - relativeY;pCkPanel.setPicCheckPanelLocation(tmpX, tmpY);  //为什么要传tmpX,而不是setX?这是我试出来的//原理我也不是很确定,如果有朋友弄清楚了,请指出setLocation(setX, setY);}}      }
}

第三个类

/*** 这个类完成了图像的对比工作* Frame传给本类一个Image,即基准的Image* 本类同时做了一次截屏,在Frame移动的过程中,本类不断地比较下方的图片和基准图像* 两者不相同的地方以蓝色显示* * 由此可以大致判断出两幅图像的区别* @author LiuCC**/
public class PicCheckPanel extends JPanel{private int width, height;  private int positionX=0, positionY=0;private BufferedImage screenImage;private BufferedImage capturedImage;private BufferedImage showedImage;private Robot myRobot;private int scrR, scrG, scrB;private int capR, capG, capB;/*** 构造函数*/public PicCheckPanel(BufferedImage inImage){this.capturedImage = inImage;this.width = inImage.getWidth();this.height = inImage.getHeight();screenImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);//showedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);try{myRobot = new Robot();}catch (Exception e) {// TODO: handle exception}screenImage=myRobot.createScreenCapture(new Rectangle(0, 0, Toolkit .getDefaultToolkit().getScreenSize().width, Toolkit .getDefaultToolkit().getScreenSize().height));              }/*** 设置Panel的位置* @param posX* @param posY*/public void setPicCheckPanelLocation(int posX, int posY){this.positionX = posX;this.positionY = posY;validate();repaint();      //这个是关键的一步,会调用paintComponent函数}/*** 设置Panel的大小* @param width* @param height*/public void setPicCheckPanelSize(int width, int height){this.width = width;this.height = height;repaint();      //这个是关键的一步,会调用paintComponent函数}/*** 最关键的,绘图函数* 对传入的图片capturedImage 与 当前在JPanel下方的图片(ScreenImage的一部分)做对比* 逐个像素比较,若相同则显示,若不相同则显示出蓝色* * 似乎完全相同是不可能的,我调试了多次,设置了一个阈值VAL,目前总体效果不错* 但是图片叠加后,特别是边缘部分还是不怎么干净,如朋友们有更好的方法,也请告诉我*/@Overridepublic void paintComponent(Graphics g){super.paintComponent((Graphics2D)g);for(int i=0; i<width; i++){for(int j=0; j<height; j++){//之前试过把 getRGB 的值直接进行比较的,但效果有问题,只好拆成R/G/B分别比较了Object scrData = screenImage.getRaster().getDataElements(i+positionX, j+positionY, null);scrR=screenImage.getColorModel().getRed(scrData);scrG=screenImage.getColorModel().getGreen(scrData);scrB=screenImage.getColorModel().getBlue(scrData);Object capData = capturedImage.getRaster().getDataElements(i, j, null);capR=capturedImage.getColorModel().getRed(capData);capG=capturedImage.getColorModel().getGreen(capData);capB=capturedImage.getColorModel().getBlue(capData);int VAL = 22; //阈值为什么选这个?只能说,这是我试验出来的//下面不得不采用了“差别不大就算相同”的判断方法if(Math.abs(scrR-capR)<=VAL&&Math.abs(scrG-capG)<=VAL&&Math.abs(scrB-capB)<=VAL)showedImage.setRGB(i, j, capturedImage.getRGB(i, j));else {showedImage.setRGB(i, j, Color.blue.getRGB());}}}g.drawImage( showedImage, // 要画的图片 0, // 目标矩形的第一个角的x坐标 0, // 目标矩形的第一个角的y坐标 width, // 目标矩形的第二个角的x坐标 height, // 目标矩形的第二个角的y坐标 0, // 源矩形的第一个角的x坐标 0, // 源矩形的第一个角的y坐标 width, // 源矩形的第二个角的x坐标 height, // 源矩形的第二个角的y坐标 this );       }
}

功能基本实现,但是还是有一些地方存在疑点,请朋友们指正

如:在第二个类中

setUndecorated(true);    

这样设置可以免去调整标题栏的麻烦,我不知道如果加入了标题栏,在之后的函数中如何处理像素点相对位置(即,我不知道如何获得标题栏的宽度),如果有朋友搞懂了,可以留言告诉我

还有一些问题就写在注释当中了,朋友们发现有问题,请留言提醒我噢。

“图片差异检查”辅助工具(即“大家来找茬”辅助工具)源码分享相关推荐

  1. 全文检索技术_中药图片拍照识别系统,爬虫、深度学习技术android app 源码分享...

    说明 当前项目是中药识别APP的后端工程,提供纯数据接口:移动端请移步中药图片拍照识别系统-移动APP端. 项目介绍 本项目包含五个模块: admin:服务器端 medicine-collection ...

  2. QQ找茬辅助工具的制作

    QQ找茬辅助工具的制作 转载请注明出处:http://www.cppblog.com/greatws/archive/2009/05/05/81996.html 这段时间GF一直在玩QQ找茬,看了一下 ...

  3. QQ找茬辅助工具 C#

    昨天晚上用C#做了一个QQ游戏"找茬"辅助工具.这个不能算是一个外挂工具,因为不涉及到服务器和客户端的问题,只涉及到图像处理的知识.目前只是实现了基本的功能,但是已经够用了,玩了几 ...

  4. 教你用python爬取王者荣耀英雄皮肤图片,并将图片保存在各自英雄的文件夹中。(附源码)

    教你用python爬取王者荣耀英雄皮肤图片,并将图片保存在各自英雄的文件夹中.(附源码) 代码展示: 保存在各自的文件夹中 美么? 让我们开始爬虫之路 开发环境 windows 10 python3. ...

  5. Windows用户层技术工具与源码分享

    Windows用户层技术工具与源码分享 文章目录 Windows用户层技术工具与源码分享 一.注入与隐藏 1.窗口界面介绍 2.远程线程注入 3.APC注入 4.突破SESSION0隔离的远程注入 5 ...

  6. Windows下最轻量级Git克隆工具源码分享

    简单的来说: 原理就是只要用户知道git地址和分支名,即可克隆项目到指定文件夹,省去了输命令行的时间,方便用户去操作 系统要求: Windows系统,然后本身环境要装有git git安装地址:http ...

  7. 2022冬-DownKyi 辅助使用的小插件源码分享

    DownKyi 辅助使用的小插件源码分享 DownKyi 是一款非常好用的b站下载软件,可以实现b站视频批量下载. 但是在大量下载后会产生缓存没有及时清理,自己写了个小插件,放在运行程序的文件夹下, ...

  8. DIY官网可视化工具打造低代码可视化一键生成导出源码工具

    DIY官网可视化工具 打造低代码可视化一键生成导出源码工具 设计一次打通设计师+产品经理+技术开发团队必备低代码可视化工具 从想法到原型到源码,一步到位低代码生成源码工具 立即定制DIY官网可视化工具 ...

  9. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

最新文章

  1. 使用VS2005调试ASP程序
  2. android 条件筛选吸顶,自定义吸顶LayoutManager
  3. Aix 6.1下安装Oracle11g详细文档
  4. movielens推荐系统_浅谈推荐系统+3个小时上手python实现(完整代码)
  5. 一个技术人的知识管理方法论
  6. RabbitMQ在Ubuntu上的环境搭建
  7. 以太坊Dapp项目-网页钱包开发手册
  8. 微信小程序 - 按照官网文档构建npm包 - 提示【没有找到可以构建的npm】
  9. JavaWeb——Get、Post请求中文乱码问题
  10. java 并发框架源码_某网Java并发编程高阶技术-高性能并发框架源码解析与实战(云盘下载)...
  11. 五种Linux桌面操作过程录制的文件
  12. 洛谷试炼场 普及常见模板
  13. 什么是0day漏洞?
  14. webrtc QOS方法四(Sender Side BWE)
  15. java eml 附件_Javamail怎样写带附件eml文件?
  16. clover懒人_懒人气球驱动的互联网毕竟可能不是一个愚蠢的想法
  17. 17_AOP入门准备_Salay案例(利用动态代理)
  18. 如何在发布宝贝页面时嵌入视频
  19. 跌才是机会,涨才是风险
  20. matlab跳动的心脏,Matlab绘制跳动的心

热门文章

  1. 汇编–从数据类型看WORD与DWORD
  2. 简单的思维导图制作过程
  3. “中国第一国门”首都机场的新Wi-Fi网:带你智能带你飞
  4. Linux VIM常用命令总结
  5. kingscada检测服务器状态,基于云服务器的状态检测平台
  6. POST常见数据提交类型
  7. 计算机病毒防护软件有哪些,电脑防护软件排名
  8. ubuntu:docker常用命令
  9. 详解OpenCV的视频背景/前景分割(背景建模/前景提取)类cv::BackgroundSubtractorKNN,并利用它实现对道路监控视频前景/背景的提取
  10. g.e-hentai 绅士爬虫