[решено] Алгоритм "шагающих квадратов" (marching squares) (как бы попроще реализовать?)

Модератор: Модераторы разделов

Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

[решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение eddy »

Для поиска изолиний на изображении я решил использовать метод "шагающих квадратов". Для выявления изолиний делаю следующее: сначала по выбранному алгоритму (линейный, логарифмический, экспоненциальный, степенной) "постеризую" изображение (т.е. разбиваю его на N уровней интенсивности); далее строю маску, как в алгоритме "шагающих квадратов":

Код: Выделить всё

    #pragma omp parallel for
    for(y = 0; y < h-1; y++){
        int x;
        unsigned char *in = levels + y*w, *out = mask + y*(w-1);
        unsigned char m, a,b,c,d;
        for(x = 0; x < w-1; x++, in++, out++){
            a = *in, b = *(in+1), c = *(in+w), d = *(in+w+1);
            m = MIN(MIN(a,b), MIN(c,d));
            *out = ((a!=m)<<3) | ((b!=m)<<2) | ((d!=m)<<1) | (c!=m);
        }
    }

Все это работает достаточно быстро (~160мс на изображении 3000x3000).
А вот с реализацией распознавания и сохранения контуров возникло непонимание:
  • Непонятно, как однопроходным алгоритмом распознать все контуры (или все-таки использовать стандартный подход и сканировать все пиксели изображения, в случае обнаружения контура проходить по контуру, обнуляя значения в уже просмотренных пикселях, возвращаться и сканировать дальше?). Еще вариант - взять "распознавалку" из лептоники, но там на выходе получится очень много данных.
  • Непонятно как замыкать контуры и "проскакивать" разрывы. Особенно если на каком-либо пикселе сходится несколько изолиний (скажем, резкий контрастный край).
  • Как сохранять обнаруженные изолинии. Я себе представляю это как массив структур. Индекс элемента массива соответствует очередному уровню интенсивности, а сам элемент - список обнаруженных изолиний. Каждый элемент списка в свою очередь является списком координат точек изолиний, полученных линейной интерполяцией из метода "шагающих квадратов". Нормально это, или можно сделать более осмысленное хранение?

Прошу совета. Может, кто-нибудь сталкивался с реализацией данного алгоритма...

P.S. Вот наброски алгоритма.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение IMB »

А Вы не смотрели OpenCV?
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение eddy »

IMB писал(а):
02.09.2011 20:19
А Вы не смотрели OpenCV?

Нет, спасибо. Мне этот тормозной монстр не нужен.
(правда, про тормознутость наслышан - сам не пробовал)

// и еще: openCV не работает с CUDA, а я все эти алгоритмы после реализации на CPU с openmp буду еще и для CUDA переводить.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение NickLion »

eddy писал(а):
02.09.2011 10:43
далее строю маску, как в алгоритме "шагающих квадратов":

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

eddy писал(а):
02.09.2011 10:43
А вот с реализацией распознавания и сохранения контуров возникло непонимание:
  • Непонятно, как однопроходным алгоритмом распознать все контуры (или все-таки использовать стандартный подход и сканировать все пиксели изображения, в случае обнаружения контура проходить по контуру, обнуляя значения в уже просмотренных пикселях, возвращаться и сканировать дальше?). Еще вариант - взять "распознавалку" из лептоники, но там на выходе получится очень много данных.
  • Непонятно как замыкать контуры и "проскакивать" разрывы. Особенно если на каком-либо пикселе сходится несколько изолиний (скажем, резкий контрастный край).
  • Как сохранять обнаруженные изолинии. Я себе представляю это как массив структур. Индекс элемента массива соответствует очередному уровню интенсивности, а сам элемент - список обнаруженных изолиний. Каждый элемент списка в свою очередь является списком координат точек изолиний, полученных линейной интерполяцией из метода "шагающих квадратов". Нормально это, или можно сделать более осмысленное хранение?

Немного с конца пойду.
3. Ну, такой способ - первое что приходит в голову.
2. Как-то не представил себе "разрыва" - откуда ему взяться?
1. Мне кажется просто идти "табличкой" и наращивать контура. Ну и сливать придётся конечно - без этого никуда.
С каждой стороной квадрата ассоциировать контура.

т.е. жёлтые - это уже рассмотренные ячейки, зелёная - сейчас рассматриваем, белые - непросмотренные. Хранить инфу надо только о ячейках раздела желтых с нежёлтыми. В зелёной ячейке мы должны слить красный и синий контура - тут вот сложность небольшая будет.

Это так пока умозрительно немного, но вроде особых подводных камней не вижу. Кроме стандартных для marching squares.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение eddy »

NickLion писал(а):
03.09.2011 14:30
Вот не уверен, что эта маска хорошо себя покажет в случае схождения нескольких изолиний.

Я тоже. В принципе, можно при проходе ячеек, в которых сходятся несколько изолиний, не обнулять их.
NickLion писал(а):
03.09.2011 14:30
2. Как-то не представил себе "разрыва" - откуда ему взяться?

А вдруг? :) (хотя, конечно, я понимаю, что разрывов быть не должно, по идее)
NickLion писал(а):
03.09.2011 14:30
Мне кажется просто идти "табличкой" и наращивать контура. Ну и сливать придётся конечно - без этого никуда.

А по-моему, как раз многопроходный алгоритм будет проще. В лептонике, например, именно так поиск связанных областей и происходит. Иначе как мы опознаем нужный контур?
Вот, например, проходим по первой строчке вашего рисунка. Получаем кусочки двух контуров. Затем проходим по второй и получаем еще три кусочка, которые надо присовокупить к другим контурам. Т.е. придется просматривать все найденные контуры с подходящими координатами края, что вполне может усложнить задачу.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение NickLion »

eddy писал(а):
03.09.2011 17:59
Вот, например, проходим по первой строчке вашего рисунка. Получаем кусочки двух контуров. Затем проходим по второй и получаем еще три кусочка, которые надо присовокупить к другим контурам. Т.е. придется просматривать все найденные контуры с подходящими координатами края, что вполне может усложнить задачу.

Не, я наверное плохо описал, что имею в виду. Постараюсь подробнее.
1. Начальная обработка - "формирование информации о разделе" :)
1.1 Проходим по верхней стороне ячеек и отмечаем начало контуров. В данном случае будет обозначен контур в ячейке 2-3 (уровень 3.5). Связываем со стороной список точек контура (чтобы не искать в дальнейшем).
2. Построчная обработка.
2.1 Вначале обрабатываем левую сторону первой ячейки. Получаем уже вторую начальную точку уровня 2.5, ассоциируем со стороой.
2.2 По каждой ячейке строки
2.2.1 Определяем направления изолиний в данной ячейке (по методу) и для каждой линии:
2.2.1.1 Если линия от обозначенной (левая/верхняя) ранее стороны к необозначенной (правая/нижняя), то это дополнение существующего контура.
2.2.1.2 Если линия от необозначенной к необозначенной - новый контур
2.2.1.3 Если от обозначенной к обозначенной, то слияние двух контуров.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение eddy »

А как определять, к какому контуру принадлежит найденный отрезок?
Я к тому, что в этом случае придется все ранее найденные контуры пробегать.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение NickLion »

eddy писал(а):
03.09.2011 18:27
А как определять, к какому контуру принадлежит найденный отрезок?
Я к тому, что в этом случае придется все ранее найденные контуры пробегать.

Нет, не надо. Со сторонами раздела и левой предыдущей стороной ассоциируем список изолиний - по уровням. Если уходит с верхней или левой берём просто список данного уровня.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение NickLion »

Т.е. примерно так. Если N уровней, ширина - W, текущая строка - i, ячейка - j.
Описание стороны раздела на полу-си-псевдокоде:
typedef list_t* side_levels_t[N];
Описание раздела:
side_levels_t up_sides[W];
Описание левой стороны:
side_levels_t left_side;

Обработка ячейки:

Код: Выделить всё

{
  side_levels_t right_side = {}, bottom_side = {};
  for( detected lines ) {
    int detected_level = ...;
    int detected_side_from = ..., detected_side_to = ...;
    list_t* link_from = NULL;
    switch( detected_side_from ) {
    case LEFT:
      link_from = left_side[detected_level];
      break;
    case UP:
      link_from = up_sides[j][detected_level];
      break;
    }
    list_t* link_to = NULL;
    switch( detected_side_to ) {
    case LEFT:
      link_to = left_side[detected_level];
      break;
    case UP:
      link_to = up_sides[j][detected_level];
      break;
    }
    if( link_from && link_to ) {
      // connect lists - link_from & link_to
    } else if( link_from ) {
      // add detected line to link_from
      switch( detected_side_to ) {
      case RIGHT:
        right_side[detected_level] = link_from;
        break;
      case DOWN:
        down_side[detected_level] = link_from;
        break;
      }
    } else if( link_to ) {
      // add detected line to link_to
      switch( detected_side_from ) {
      case RIGHT:
        right_side[detected_level] = link_to;
        break;
      case DOWN:
        down_side[detected_level] = link_to;
        break;
      }
    } else {
      // new list, e.g.
      list_t* new_list = ...;
      switch( detected_side_from ) {
      case RIGHT:
        right_side[detected_level] = new_list;
        break;
      case DOWN:
        down_side[detected_level] = new_list;
        break;
      }
      switch( detected_side_to ) {
      case RIGHT:
        right_side[detected_level] = new_list;
        break;
      case DOWN:
        down_side[detected_level] = new_list;
        break;
      }
    }
  }
  //
  up_sides[j] = down_side;
  left_side = right_side;
}

Это "примерно", естественно.

UPD забыл обновить разделы-стороны :)
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение NickLion »

PS через массив на 4 элемента (на каждую из сторон) можно убрать switch - не уверен, что быстрее будет, но красивее - точно, но уже лень переделывать :)
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение eddy »

Читал-читал ваш алгоритм, да так и не понял.
Получается, что мы заводим массив "предыдущих" изолиний, длиной в одну строку изображения, куда заносим сведения об уже просмотренных пикселях. Если через пиксель проходит изолиния, в него помещается указатель на последнюю точку соответствующей этой изолинии структуры. При обнаружении очередной точки, к соответствующей структуре добавляется еще одна точка. Так?
В принципе, разумно. Тогда после окончания прохода по изображению и составления контуров, мы проходим по каждому обнаруженному контуру и на его основе создаем структуру, содержащую в себе координаты точек этой изолинии, уточненные методом линейной интерполяции.

Что же, попробую сделать так.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение NickLion »

eddy писал(а):
04.09.2011 14:11
Получается, что мы заводим массив "предыдущих" изолиний, длиной в одну строку изображения, куда заносим сведения об уже просмотренных пикселях. Если через пиксель проходит изолиния, в него помещается указатель на последнюю точку соответствующей этой изолинии структуры. При обнаружении очередной точки, к соответствующей структуре добавляется еще одна точка. Так?

Ну, вроде да. Только хранить указатель на последнюю точку или на весь список - тут надо подумать.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение eddy »

NickLion, предложенный вами алгоритм я все-таки не смог реализовать. Сделал наброски как бы шагающих квадратов. Глюков пока полным-полно, зато работает быстро :)
Постараюсь в ближайшее время все глюки убрать (пока самый главный глюк - расчет координат очередного узла изолинии, который производится мною совершенно неправильно). Надеюсь, придумаю как в квадрате из четырех точек рассчитать координаты одной точки, лежащей где-нибудь в районе середины пересечения плоскости уровня и поверхности изображения.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: [решено] Алгоритм "шагающих квадратов" (marching squares)

Сообщение eddy »

Решено!
Завтра доделаю мелкие "косяки" (чтобы можно было не только линейную, но и нелинейную шкалу высот иметь) и сделаю commit.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали: