“图片差异检查”辅助工具(即“大家来找茬”辅助工具)源码分享
2012-04-19 10:35
495 查看
忽然心血来潮,想写一个辅助工具,让朋友们在“大家来找茬”之类的游戏中可以少费一些眼睛。
在Java方面我是新手,在折腾了一段时间后,终于还是写出了一个基本可用的测试版程序。详细的使用方法和使用效果,可以参见这个博客http://blog.sina.com.cn/s/blog_9245c9e0010136by.html
这个小程序也可以通过这种方法进行下载——CSDN的资源中搜索“自己用Java写的图像比较器”
这里我想和大家分享一下编写过程中遇到的问题,同时也把源代码公布于此,朋友们可以批评指正。
思路:
类似于“大家来找茬”的游戏,可以用这样的思路来破解:
1.截取第一幅图A
2.截图第二幅图B
3.通过逐个像素比较,将差异部分显示出来
但是实际编程时,发现了问题——你很难确保两次截图时的大小位置完全一致!
开始我还想着通过算法来判断,后来想到了一个好解法——机器是死的,人是活的啊。所以步骤2可以调整为“将第一幅截图显示在窗口中,通过人工移动窗口,将两幅图叠加”
源码分享:
最终的程序由三个类构成
myScreenCapture:实现截图功能,并把截图的数据传给PicCheckFrame
PicCheckFrame:创建一个新窗口,用户可以手工移动该窗口,实现两幅图片的叠加
PicCheckPanel:实现步骤3中的算法,显示两幅图片的差异
第二个类
第三个类
功能基本实现,但是还是有一些地方存在疑点,请朋友们指正
如:在第二个类中
这样设置可以免去调整标题栏的麻烦,我不知道如果加入了标题栏,在之后的函数中如何处理像素点相对位置(即,我不知道如何获得标题栏的宽度),如果有朋友搞懂了,可以留言告诉我
还有一些问题就写在注释当中了,朋友们发现有问题,请留言提醒我噢。
在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 stub new 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() { @Override public 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() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub System.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)); } @Override public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub tempX = e.getX(); tempY = e.getY(); } @Override public void mouseReleased(MouseEvent e) { // TODO Auto-generated method stub //Do Nothing! } @Override public void mouseDragged(MouseEvent e) { // TODO Auto-generated method stub startX=tempX; startY=tempY; endX=e.getX(); endY=e.getY(); repaint(); //重绘图像,以显示动态效果 } @Override public void mouseMoved(MouseEvent e) { if(selectRectangle.contains(e.getPoint())){ setCursor(new Cursor(Cursor.MOVE_CURSOR)); //在选定区域内外,采用不同的鼠标样式以示区别 }else{ setCursor(specialCursor); } } @Override public 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 block e1.printStackTrace(); } //接下来启动对比图片所需的Frame new PicCheckFrame(selectImage); }else{//在选定区域外双击,则重新选择 startX=0; startY=0; endX=0; endY=0; selectRectangle=new Rectangle(0,0,0,0); repaint(); } } } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } @Override public 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() { @Override public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub System.exit(0); } }); addMouseListener(new mouseFunction()); addMouseMotionListener(new mouseMotionFunction()); setSize(capturedImage.getWidth(), capturedImage.getHeight()); setVisible(true); } /** * * @author LiuCC * */ private class mouseFunction extends MouseAdapter{ @Override public void mousePressed(MouseEvent e){ if(1==e.getClickCount()){ mousePressedNow=true; relativeX = e.getX(); relativeY = e.getY(); //e带来的是,鼠标对于当前Component的相对位置 } } @Override public void mouseReleased(MouseEvent e){ mousePressedNow=false; } } private class mouseMotionFunction extends MouseMotionAdapter{ @Override public 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,目前总体效果不错 * 但是图片叠加后,特别是边缘部分还是不怎么干净,如朋友们有更好的方法,也请告诉我 */ @Override public 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);
这样设置可以免去调整标题栏的麻烦,我不知道如果加入了标题栏,在之后的函数中如何处理像素点相对位置(即,我不知道如何获得标题栏的宽度),如果有朋友搞懂了,可以留言告诉我
还有一些问题就写在注释当中了,朋友们发现有问题,请留言提醒我噢。
相关文章推荐
- QQ游戏辅助工具-大家来找碴(附源码)
- QQ游戏辅助工具-大家来找碴(附源码)
- 对"QQGame-大家来找茬"的辅助工具的改进
- 自编小工具:Collect-便利你的阅读摘录收集, 含源码,分享给大家
- 用Python实现QQ游戏大家来找茬辅助工具
- 大家来找茬辅助工具超级简易版
- 用Python实现QQ游戏大家来找茬辅助工具
- 好工具大家分享
- 一款生活辅助工具应用源码完整版
- 分享两个超棒的帮助你生成占位图片(placeholder image)的在线工具
- Android gallery实现图片的左右循环旋转源码分享
- 分享:动态添加控件和公用章水印工具 提供源码下载
- 通过辅助工具进行安卓 Toast 文本检查的方法
- 分享:录制gif小图片工具
- 图片上传的一个类 分享给大家
- jquery图片幻灯片殊效教程源码分享_Javascript教程
- 向大家推荐一些编写SQL时带智能提示的辅助工具
- 给大家分享下坐标转换的代码的JS和Python两个版本的源码【转】
- iPhone、iPad 网页抓取工具源码分享
- php+flash+jQuery多图片上传源码分享