对于图像中的每一行,我想找到该行中的第一个黑色(或第一个非白色)像素。例如,对于这样的图像:
我希望输出如下:
0
1
0
或者接近我能解析的东西。我认为可能有一种方法可以用subimage-search做到这一点,但我不太清楚如何做。有什么指针吗?
答案 0 :(得分:5)
您不需要 subimage-search 来实现目标。问题可以简化为 文本解析 。
考虑这一点:您可以告诉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
这意味着:
其他行由4列组成:
(N,M)
的第一列表示各个像素的确切位置为(row_number,column_number)
。 (行号和列号的索引从零开始 - 第1行表示为0
,第2行表示为1
。) 作为旁注:你可以使用原始图像的这种文本表示(有或没有一些额外的修改)来重新创建一个真实的图像:
convert textwizard.txt wizard.jpg
您应该知道可以使用以下语法选择图像的特定区域:
image.png[WIDTHxHEIGHT+X_OFFSET+Y_OFFSET]
因此,要仅选择特定行,您可以将HEIGHT
设置为1
。要完全获取任何行,请将X-OFFSET
设置为0
。要获取特定行,请相应地设置Y-OFFSET
。
为了获得索引为47的行的值(对于上面使用的内置wizard:
图像),我们可以这样做:
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:-
根据以上信息片段,可以采用此任务的方法很明确:
以下是可以使用的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个匹配项(即第一个匹配项)。
它会很慢,但它会起作用。
答案 1 :(得分:3)
我会使用内置的棋盘图案来完成这样的事情:
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提示。