Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Поиск картинок на экране


Автор: Anyone 23.11.2010, 14:15
Работаю с Веб-приложением и всю монотонную работу хочу переложить на плечи процессора.
Требуется найти определенные кнопки на экране и по алгоритму выполнять их нажатие в нужный момент.
Проблема в том, что поиск заданной картинки по образцу происходит очень медленно.
Все картинки-образцы я храню как массив типа байт, а вот скриншот в массив записать не удалось - программа загружает процессор на 50% и работает оч длительное время (не могу дождаться даже). 
Есть какая-нить другая идея, кроме поиск подмассива в массиве и использования функции: robot.getPixelColor(x,y).getRGB()?

Вот кусок кода, на котором зависает приложение (2я строка):
Код

        BufferedImage bufferedImage = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
        byte [] t = ((DataBufferByte)(bufferedImage).getRaster().getDataBuffer()).getData();

Автор: Temdegon 23.11.2010, 23:22
Если нужно покликать кнопки в веб-приложении, может не нужно и городить этот огород с распознаванием и роботом? 
Вот к примеру эта либа http://htmlunit.sourceforge.net/ вполне неплохо выполняет функции браузера. Можно без проблем найти кнопку по id и вызвать на ней button.click()
вот пример сабмита формы из их getting started:
Код

public void submittingForm() throws Exception {
    final WebClient webClient = new WebClient();

    // Get the first page
    final HtmlPage page1 = webClient.getPage("http://some_url");

    // Get the form that we are dealing with and within that form, 
    // find the submit button and the field that we want to change.
    final HtmlForm form = page1.getFormByName("myform");

    final HtmlSubmitInput button = form.getInputByName("submitbutton");
    final HtmlTextInput textField = form.getInputByName("userid");

    // Change the value of the text field
    textField.setValueAttribute("root");

    // Now submit the form by clicking the button and get back the second page.
    final HtmlPage page2 = button.click();

    webClient.closeAllWindows();
}

Автор: Anyone 24.11.2010, 01:20
Спасибо за пример, может пригодится для другой задачи, но не этой.
Единственный вопрос: возможно ли с помощью этой либы получить доступ к таблице, которая является отдельной компонентой, используемой в JSF и полистать странички, покликать по ячейкам? 

По поводу сабжа. Я думал о написании своего браузера, но проблем 3:
1. Протокол не http, a https, в браузер необходимо импортировать сертификаты;
2. Кнопочки (они выгружают файлы) находятся в колонке многостраничной таблицы, причем на каждой странице может быть разное их ко-во а потому и расположение тоже разное (пустые строки имеют меньшую высоту и в этом вся проблема, иначе задачу б давно решил);
3. Все кнопки имеют одинаковое название.

У меня такое подозрение, что без нейронных сетей эту задачу не выполнить...

Автор: Temdegon 24.11.2010, 01:40
Мммда. Какие-то непонятные у вас желания - браузеры писать, образы распознавать...
Напишите конкретно, для чего вам это нужно. Нужно скачать все файлы с какого-то сайта? или вы хотите написать браузер, что бы тестировать свое веб-приложение?
Вот там же пример обхода таблицы
http://htmlunit.sourceforge.net/table-howto.html
Я с этой либой работал очень поверхностно, в дебри не влезал. Что она может и что не может разбирайтесь уж сами. Ну или пишите браузер или распознаватель образов  smile 

Автор: Anyone 24.11.2010, 09:20
Цитата(Temdegon @  24.11.2010,  01:40 Найти цитируемый пост)
Нужно скачать все файлы с какого-то сайта?

Да, именно это.
Если либа умеет работать с протоколом https и можно импортировать сертификаты - можно ее попробывать.

Автор: Anyone 24.11.2010, 13:37
Все рисунки, в том числе скрин экрана, представляю как массив байтов с помощью функции:
Код

    public byte [] ImageToBytesArray(BufferedImage bufferedImage, String type){
        byte[] t=null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try{
            ImageIO.write(bufferedImage, type, baos );
            baos.flush();
            t = baos.toByteArray();
            baos.close();
        }catch(Exception e){
            System.out.println(e);
        }
        return t;
    }

Для поиска координат изображения ищу подмассив в массиве. Работает очень быстро, но ничего не нахожу. Может так вообще делать нельзя?
Код

private boolean FindNextButton(){
...
                boolean result=false;
        BufferedImage bufferedImage = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
        byte [] t = ImageToBytesArray(bufferedImage,"bmp");
        for (int i=0;i<t.length;i++){
            if (isImageOnScreen(i,t,pixelArrayNext, biNext.getWidth(), biNext.getHeight())){
                result=true;
                                ...
            }
  return result;
}

    private boolean isImageOnScreen(int index, byte [] arrayScreenPixels, byte [] arrayImagePixels, int width, int height){
        for (int i=0; i<arrayImagePixels.length; i++){
            if (arrayImagePixels[i]!=arrayScreenPixels[i+index])
                return false;
        }
        return true;
    }

Автор: jk1 24.11.2010, 14:27
Откуда берется pixelArrayNext и что он из себя представляет?

Автор: Anyone 24.11.2010, 14:45
Цитата(jk1 @  24.11.2010,  14:27 Найти цитируемый пост)
Откуда берется pixelArrayNext и что он из себя представляет? 


Это искомая картинка, представленна как массив байов:

Код

BufferedImage biNext;
byte[] pixelArrayNext;
...
file = new File(PATH_NEXT_IMAGE);
if (file.exists())
  biNext = ImageIO.read(file);
else return;
//pixelArrayNext = ((DataBufferByte)(biNext).getRaster().getDataBuffer()).getData();
pixelArrayNext = ImageToBytesArray(biNext,"bmp");



Очевидно, что функция ImageToBytesArray() возвращает массив байтов не самой картинки, а объекта типа BufferedImage.
Как получить необходимый массив?
Тот способ, что в ремарках - очень медленный для всего разрешения экрана и потому не подходит:
//pixelArrayNext = ((DataBufferByte)(biNext).getRaster().getDataBuffer()).getData();

Автор: jk1 24.11.2010, 15:10
Формат BMP помимо прочего содержит заголовок и палитру, может даже быть сжат (http://ru.wikipedia.org/wiki/RLE), поэтому искать подмассив в массиве просто считав два файла бессмысленно. Небольшой пример:

Полное изображение: Заголовок + некоторая часть изображения + то, что мы пытаемся распознать + еще что-то

Ваш шаблон, который вы пытаетесь обнаружить: Заголовок + то, что мы пытаемся распознать.

Очевидно, что второго в первом Вы не найдете.

Автор: Temdegon 24.11.2010, 15:19
Цитата

Да, именно это.
Если либа умеет работать с протоколом https и можно импортировать сертификаты - можно ее попробывать. 

Я на 99% уверен, что умеет. Но лично за вас искать не буду. Если вам кажется, что написать свой браузер будет проще и надежнее, чем рассмотреть другие варианты - ваше дело.
Кроме этого, есть масса других способов. Можно внимательно посмотреть html и javascript страницы, и понять, как он устроен. Я знаю десятки примеров, когда люди на PHP+curl делали клоны очень сложных сайтов. И это при том, что курлу до htmlUnit как до луны пешком.
Вот еще вам пища для размышлений:
Код

javascript:allLinks=document.getElementsByTagName('a'); str=""; for (i=0; i<allLinks.length; i++) {str+=allLinks[i]+"\n";}; alert(str);

вставьте в строку браузера, нажмите enter и получите все ссылки с открытой страницы . Очевидно, что даже таким убогим способом можно выдрать с любой страницы любые данные. А если поставить к браузеру FireBug, то возможности вообще безграничны

Автор: Anyone 25.11.2010, 13:50
Нашел http://www.dreamincode.net/forums/topic/157963-finding-a-sub-image-in-a-larger-image/ похожей задачи. Попробую и отпишусь.

Автор: Anyone 25.11.2010, 14:44
Поиск происходит мгновенно, всем спасибо!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)