Найдите первый черный пиксель в каждой строке с помощью ImageMagick

Для каждой строки изображения я хотел бы найти первый черный (или первый небелый) пиксель в этой строке. Например, для такого изображения:

введите здесь описание изображения

Я бы ожидал вывода, например:

0
1
0

Или что-то близкое к тому, что я могу разобрать. Я думаю, что может быть способ сделать это с помощью поиска по субизображениям, но я не совсем знаю, как это сделать. Любые указатели?


person pjreddie    schedule 05.03.2015    source источник
comment
Итак, что вы хотите вернуть для ряда белых пикселей?   -  person Kurt Pfeifle    schedule 05.03.2015
comment
Сам вопрос вертелся у меня на языке :-)   -  person Mark Setchell    schedule 05.03.2015


Ответы (2)


Вам НЕ нужен subimage-search для достижения вашей цели. Проблема может быть сведена к разбору текста.

1. Основы

Учтите следующее: вы можете указать ImageMagick преобразовать любое изображение в текстовое представление, которое содержит точную информацию о цвете для каждого отдельного пикселя. Пример:

convert wizard: textwizard.txt

(wizard: — это встроенный образ, доступный для всех установок ImageMagick в целях тестирования.)

Да, это так просто! Этот «формат» изображения запрашивается простым добавлением суффикса .txt. Результаты:

# ImageMagick pixel enumeration: 480,640,255,srgb
0,0: (255,255,255)  #FFFFFF  white
1,0: (255,255,255)  #FFFFFF  white
2,0: (255,255,255)  #FFFFFF  white
[....]
47,638: (246,247,249)  #F6F7F9  srgb(246,247,249)
48,638: (246,247,249)  #F6F7F9  srgb(246,247,249)
47,639: (236,235,236)  #ECEBEC  srgb(236,235,236)
48,639: (230,228,218)  #E6E4DA  srgb(230,228,218)
[....]
476,639: (255,255,255)  #FFFFFF  white
477,639: (255,255,255)  #FFFFFF  white
478,639: (255,255,255)  #FFFFFF  white
479,639: (255,255,255)  #FFFFFF  white

Если вы посмотрите на первую строку вывода, вы заметите, что ImageMagick использует ее для детализации некоторой специальной информации об изображении:

# ImageMagick pixel enumeration: 480,640,255,srgb

Это означает:

  • изображение имеет ширину 480 пикселей,
  • изображение имеет высоту 640 пикселей,
  • изображение использует диапазон 0-255 для информации о цвете на канал (что эквивалентно 8-битной глубине цвета),
  • изображение построено в цветовом пространстве sRGB

Остальные строки состоят из 4 столбцов:

  1. первый столбец в формате (N,M) указывает точное положение соответствующих пикселей как (row_number,column_number). (Индекс для номеров строк и столбцов начинается с нуля: строка № 1 обозначается как 0, № 2 — как 1.)
  2. остальные три столбца, избыточно, содержат одну и ту же информацию, каждый в другой нотации: точное значение цвета для пикселя, указанное в столбце 1. (Последний столбец будет использовать удобочитаемое имя, если ImageMagick знает его для этого значения цвета...)

В качестве примечания: вы можете использовать такое текстовое представление исходного изображения (с некоторыми дополнительными модификациями или без них), чтобы воссоздать реальное изображение:

convert textwizard.txt wizard.jpg

2. Выберите конкретную строку

Вы должны знать, что вы можете выбрать определенную область изображения с помощью следующего синтаксиса:

image.png[WIDTHxHEIGHT+X_OFFSET+Y_OFFSET]

Таким образом, чтобы выбрать только определенную строку, вы можете установить HEIGHT как 1. Чтобы получить любую строку полностью, установите X-OFFSET как 0. Чтобы получить конкретную строку, установите Y-OFFSET соответственно.

Чтобы получить значения (для встроенного изображения wizard:, использованного выше) для строки с индексом 47, мы можем сделать:

convert wizard:[640x1+0+47] row47.txt

cat row47.txt
 # ImageMagick pixel enumeration: 480,1,255,srgb
 0,0: (255,255,255)  #FFFFFF  white
 1,0: (255,255,255)  #FFFFFF  white
 2,0: (255,255,255)  #FFFFFF  white
 [....]
 428,0: (82,77,74)     #524D4A  srgb(82,77,74)
 429,0: (169,167,168)  #A9A7A8  srgb(169,167,168)
 430,0: (232,231,228)  #E8E7E4  srgb(232,231,228)
 432,0: (246,247,249)  #F6F7F9  srgb(246,247,249)
 [....]
 476,0: (255,255,255)  #FFFFFF  white
 477,0: (255,255,255)  #FFFFFF  white
 478,0: (255,255,255)  #FFFFFF  white
 479,0: (255,255,255)  #FFFFFF  white

Если вы не хотите, чтобы текст выводился в файл, а печатался на стандартном канале вывода, вы можете сделать это:

convert wizard:[480x1+0+47] txt:-

3. Сшиваем все вместе

Основываясь на приведенных выше фрагментах информации, подход, который можно использовать для этой задачи, ясен:

  1. Перебрать все строки пикселей изображения.
  2. Выведите значение цвета каждого пикселя в виде текста.
  3. Найдите первый небелый пиксель и сохраните информацию о его местоположении.

4. Возможный скрипт (OS X, Linux, Unix)

Вот основная часть скрипта Bash, который можно использовать:

# Define some image specific variables (width, height, ...)
image=${1}
number_of_columns=$(identify -format '%W' ${image}) 
width=${number_of_columns}                        # just an alias
number_of_rows=$(identify -format '%H' ${image})
height=${number_of_rows}                          # just an alias
max_of_indices=$(( ${height} -1 ))

# Loop through all rows and grep for first non-white pixel
for i in $(seq 0 ${max_of_indices}); do
   echo -n "Row ${i} :  " ;
   convert ${image}[${width}x1+0+${i}] txt:- \
     | grep -v enumeration                   \
     | grep -v '#FFFFFF' -m 1                \
   || echo "All WHITE pixels in row!"  
done

-v white отменит выбор всех строк, содержащих строку white. Параметр -m 1 вернет максимум 1 совпадение (т. е. первое совпадение).

Это будет медленно, но сработает.

person Kurt Pfeifle    schedule 05.03.2015
comment
Спасибо, я пошел с аналогичным подходом, вместо этого я использую python, потому что я уже делал там кучу других вещей. Тем не менее, обработка текста, безусловно, подходит для этого. - person pjreddie; 05.03.2015

Я бы сделал что-то подобное, используя встроенный шаблон шахматной доски:

convert -size 100x100 pattern:checkerboard -auto-level board.png

введите здесь описание изображения

#!/bin/bash
convert wizard: txt: | awk -F'[,: ]' '
   /^#/ || /#FFFFFF/ {next}
   !($2 in fb)       {fb[$2]=$1}
   END               {r=$2;for(i=0;i<=r;i++){if(i in fb)print i,fb[i]; else print i,"-1"}}'

-F[,: ] говорит awk разделить слова в строке запятыми, двоеточиями или пробелами - это помогает мне добраться до строки и столбца в начале каждой строки. Строка с /^#/ пропускает комментарий в первой строке текстового вывода ImageMagick и во всех строках, содержащих white или #FFFFFF.

Затем у меня есть массив fb[] , индексированный по строке изображения, который содержит столбец первого черного пикселя в каждой строке. Каждый раз, когда я нахожу строку со строкой, которой нет в моем массиве fb[], я сохраняю ее в массиве.

В конце, внутри END{}, я запускаю fb[], печатая все строки и индексы первых черных пикселей в этих строках. Обратите внимание, что я вывожу -1 вместо любых неопределенных элементов (то есть тех, у которых нет небелых пикселей) - спасибо @KurtPfeifle за подсказку.

person Mark Setchell    schedule 05.03.2015
comment
Марк, это работает с изображениями не в шахматном порядке? - person Kurt Pfeifle; 05.03.2015
comment
@KurtPfeifle Теперь это так - спасибо! Я тестировал с convert rose: -threshold 50% -flop txt: ... - person Mark Setchell; 05.03.2015
comment
Проверь это с wizard:, Марк. Ваш дает совершенно другой результат, чем мой. Ваш возвращает только XXX 0 для меня. - person Kurt Pfeifle; 05.03.2015
comment
Если я создам файл с convert rose: -threshold 50% -flop rose-bw-flopped.png, это будет изображение 70x46, ваш скрипт не сработает. - person Kurt Pfeifle; 05.03.2015
comment
Упс, у меня сегодня inverted день! Все началось, когда OP запросил первый черный (или небелый) пиксель... Теперь я думаю, что это правильно, спасибо. - person Mark Setchell; 05.03.2015
comment
Но это не работает для любого входного файла (пока) - только для самостоятельно созданной шахматной доски... :-) Кстати, я также инвертировал s.th. ранее - поменял местами строки и столбцы в более ранней версии моего ответа. ;-) - person Kurt Pfeifle; 05.03.2015
comment
Я втягиваюсь в настоящее кодирование sed/awk, как только оно выходит за рамки самой простой задачи, и никогда бы не подумал о том, чтобы делать то, что вы сделали в этом ответе. -- Может быть, поэтому я не могу заставить ваше решение работать? (Я ожидаю, что он будет работать с любым изображением, а не только с черно-белым.) Если я изменю board.png в вашем сценарии awk на любое другое изображение, мой и ваши решения дают очень разные результаты. Я думаю, что в вашем скрипте есть еще один недостаток: он возвращает все 0 даже для изображения шахматной доски. - person Kurt Pfeifle; 05.03.2015
comment
@KurtPfeifle Мммм, мне кажется, это работает. -auto-level важно сделать ваше белое белье белым — звучит как реклама стирального порошка! - person Mark Setchell; 05.03.2015
comment
Как я уже сказал, я ожидаю, что решение будет работать для любого изображения. Задача состоит в том, чтобы найти первый небелый пиксель в каждой строке. - person Kurt Pfeifle; 05.03.2015
comment
На сегодня позитивно все! Спасибо Курт :-) - person Mark Setchell; 05.03.2015
comment
Хороший код. Как изменить его, чтобы печатать также последний черный пиксель в каждой строке? Например, 30 2 10 означает, что в строке 30 первый черный пиксель находится в столбце 2, а последний — в столбце 10. Также я хотел бы сохранить разницу 10-2=8. Спасибо. - person Sigur; 18.03.2017