Определите, равны ли изображения системного буфера обмена - PullRequest
10 голосов
/ 26 августа 2011

Я не уверен, что моя проблема зависит от платформы, но я думаю, что это не так.
Потому что мой опыт основан на специфике Windows java.awt.Toolkit и Windows-буфере обмена.

В следующем примере класса показана проблема, с которой я столкнулся.
ПРИМЕЧАНИЕ: Перед запуском программы убедитесь, что в системном буфере обмена нет изображения.

Если в системном буфере обмена нет изображения, программа помещает в него новый снимок экрана.

Тогда я получаю данные буфера обмена два раза!

Все 3 изображения равны! - оригинальный скриншот и каждое изображение, которое я получаю из буфера обмена.
что нормально.

Но теперь программа запускается во второй раз. ПРИМЕЧАНИЕ: В буфере обмена есть старый скриншот!

Программа генерирует новый скриншот и дважды получает старый из буфера обмена.

Нет изображения равно любому! - Первый (новый скриншот) должен быть не равным, все нормально

Но каждое следующее изображение, которое я получаю, не равно.

Q1: Если каждое следующее изображение, которое я получаю, не равно, почему оно было равным в первый раз?
Q2: Большой вопрос: Как я могу сравнить java.awt.Image, чтобы получить каждое следующее изображение равным.

Я ищу простое и быстрое сравнение двух изображений или простой способ выяснить, что буфер обмена не изменился.

public class Example {

    public static void main( String[] args ) throws Exception {

        final Toolkit   toolkit   = Toolkit.getDefaultToolkit();
        final Clipboard clipboard = toolkit.getSystemClipboard();
        final Image     origImage = new Robot().createScreenCapture( new Rectangle( toolkit.getScreenSize() ) );

        if( !clipboard.isDataFlavorAvailable( DataFlavor.imageFlavor )
            || clipboard.getData( DataFlavor.imageFlavor ) == null ) {
            clipboard.setContents( new ImageSelection( origImage ), null );
        }

        Image clipImage1 = (Image)clipboard.getData( DataFlavor.imageFlavor );
        Image clipImage2 = (Image)clipboard.getData( DataFlavor.imageFlavor );

        System.out.println(origImage.hashCode());
        System.out.println(clipImage1.hashCode());
        System.out.println(clipImage2.hashCode());
        System.out.println(clipImage1.equals( clipImage2 ));


    }

    public static class ImageSelection implements Transferable {
        private Image image;
        public ImageSelection(Image image) {
            this.image = image;
        }
        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{DataFlavor.imageFlavor};
        }
        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return DataFlavor.imageFlavor.equals(flavor);
        }
        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            if (!DataFlavor.imageFlavor.equals(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return image;
        }
    }    
}

Ответы [ 4 ]

7 голосов
/ 30 августа 2011

Q1: ссылки на объекты в первом случае были одинаковыми.

Q2: вот способ проверить, равны ли данные на снимках экрана:

  //createScreenCapture() returns BufferedImage which is more useful for what you are doing.

  static boolean bufferedImageEquals( BufferedImage b1, BufferedImage b2 ) {
    if ( b1 == b2 ) {return true;} // true if both are null
    if ( b1 == null || b2 == null ) { return false; }
    if ( b1.getWidth() != b2.getWidth() ) { return false; }
    if ( b1.getHeight() != b2.getHeight() ) { return false; }
    for ( int i = 0; i < b1.getWidth(); i++) {
     for ( int j = 0; j < b1.getHeight(); j++ ) {
       if ( b1.getRGB(i,j) != b2.getRGB(i,j) ) { 
           return false;
       }
      }
    }
    return true;
  }
2 голосов
/ 04 сентября 2011

Если вы хотите сравнить экраны, есть несколько способов:

  1. Чтобы преобразовать его в байтовый массив и сравнить массивы
  2. Для вычисления MD5-хеша

И вы даже можете сохранить эти массивы в pngs / jpg, чтобы проверить, что не так с логикой.

        BufferedImage clipImage1 = (BufferedImage) clipboard
            .getData(DataFlavor.imageFlavor);
    RenderedImage renderclipImage1 = createImage(clipImage1);
    File clipImage1png = new File("clipImage1.png");
    ImageIO.write(renderclipImage1, "png", clipImage1png);
    byte[] clipeImage1Bytes = bufImageToBytesConverter(clipImage1);
    MessageDigest mdInst1 = MessageDigest.getInstance("MD5");
    mdInst1.update(clipeImage1Bytes);
    byte[] md5hashClipImage1 = mdInst1.digest();
    System.out.println(returnHex(md5hashClipImage1));

    BufferedImage clipImage2 = (BufferedImage) clipboard
            .getData(DataFlavor.imageFlavor);
    RenderedImage renderclipImage2 = createImage(clipImage2);
    File clipImage2png = new File("clipImage2.png");
    ImageIO.write(renderclipImage2, "png", clipImage2png);
    byte[] clipImage2Bytes = bufImageToBytesConverter(clipImage2);
    MessageDigest msInst2 = MessageDigest.getInstance("MD5");
    msInst2.update(clipImage2Bytes);
    byte[] md5hashClipImage2 = msInst2.digest();
    System.out.println(returnHex(md5hashClipImage2));

Вывод будет иметь вид

nulle5c49978317c0151969cf63f212f7662
nulle5c49978317c0151969cf63f212f7662

Если бы вы уточнили контекст больше, вы могли бы получить больше ответов.Например, это для совместного использования удаленного рабочего стола и т. Д ..

2 голосов
/ 03 сентября 2011

В дополнение к ответу Клинта вы должны преобразовать изображение буфера обмена в BufferedImage, если вы хотите сравнить их.

public static BufferedImage convert(Image img)
{
    BufferedImage i = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR_PRE);
    Graphics2D g = i.createGraphics();
    g.drawImage(img, 0, 0, null);
    g.dispose();
    return i;
}

Используйте этот метод для преобразования двух изображений в BufferedImages и сравнения ихКлинт отправил.

2 голосов
/ 29 августа 2011

Что ж, я взглянул на исходный код JDK, который мы используем в нашей IDE (это IBM JDK). java.awt.Image выглядит так, как будто это абстрактный класс и в нем не определено значение equals (пожалуйста, проверьте ваш JDK). Поскольку это так, либо подкласс должен реализовывать equals, либо мы возвращаемся к java.lang.Object.equals(java.lang.Object), который согласно нашему JDK реализует метод equals как return this == arg.

Опять же, пожалуйста, подтвердите это с вашим JDK. Но вот что я бы предположил, если бы ваш JDK и наш «соответствовали» реализации. Если используется Object equals, то я предполагаю, что в первый раз в вашей программе JVM отслеживает объекты в буфере обмена и может сказать, что это одно и то же изображение. Но когда JVM завершает и затем перезапускает, он больше не может знать, были ли это один и тот же объект или нет. Поэтому он присваивает им разные пространства памяти (т. Е. Ссылки) как разные объекты и, таким образом, они больше не равны.

Конечно, не зная, какие подклассы используются или если ваш JDK / JVM реализован по-другому, я не могу сказать это с полной уверенностью. Но это кажется весьма вероятным, особенно потому, что буфер обмена технически находится вне JVM и доступен через JVM. Как JVM может сказать, что есть, а что нет, если что-то уже есть? Если кто-то не реализует способ сказать, что опирается на знание ОС, я думаю, что это невозможно.

Таким образом, это означает, что вам лучше всего внедрить собственное решение, в котором Клинт, похоже, неплохо справился.

...