imagemagick获得具有最常见颜色的像素的坐标

时间:2012-08-13 21:12:41

标签: imagemagick


简而言之,我想找出一个主色的像素的坐标。

具体来说,我希望实现以下目标:

  1. 找到主要颜色。占主导地位的是指图像中大部分像素的颜色(我使用直方图实现了它)

  2. 获得这种颜色(在我的情况下是黑色)后,我想找到一个黑色的像素,它只被黑色像素包围。基本上是黑色最集中区域的中心。

  3. 直到现在,我才能获得主要颜色。

    convert src.png -format %c histogram:info: > x.txt
    cat x.txt | awk '{print $1}' | sed 's/://g' > x1.txt
    h=$(sort -n x1.txt | tail -1)
    cat x.txt | grep "$h"
    rm -rf x.txt
    

    结果:

        169211: (  0,  0,  0,255) #000000 black
    

    现在,我也可以获得黑色的所有坐标

    convert src.png txt: | grep black
    469,799: (  0,  0,  0,255)  #000000  black
    470,799: (  0,  0,  0,255)  #000000  black
    471,799: (  0,  0,  0,255)  #000000  black
    472,799: (  0,  0,  0,255)  #000000  black
    473,799: (  0,  0,  0,255)  #000000  black
    474,799: (  0,  0,  0,255)  #000000  black
    475,799: (  0,  0,  0,255)  #000000  black
    476,799: (  0,  0,  0,255)  #000000  black
    477,799: (  0,  0,  0,255)  #000000  black
    478,799: (  0,  0,  0,255)  #000000  black
    ...
    

    但是我需要一个黑色像素的随机坐标,它位于一个只有黑色像素的地方......

    我正在使用Linux和Imagemagick版本6.6.5

4 个答案:

答案 0 :(得分:4)

(更新以消除一些弱点)

首先,找到你的主要颜色。您似乎已经知道如何执行此操作,因此我跳过此步骤。 (我也没有检查或验证你的代码......)

您的代码存在一些缺点:

  1. 您只清理x.txt,而不是x1.txt

  2. 您应该从第二个命令中删除| sed 's/://g'部分。它会从您的变量中删除:冒号,但这会导致h=21(而不是h=21:)导致您grep "$h"找到所有这些行:

     1: (221, 86, 77) #DD564D srgb(221,86,77)
     1: (221,196,192) #DDC4C0 srgb(221,196,192)
     1: (221,203,197) #DDCBC5 srgb(221,203,197)
    [...] 
    21: (255,255,255) #FFFFFF white
    

    如果你保持h=21:,你会找到你要找的那一行! (验证示例:使用内置的rose:图片代替src.png来查看我的意思。)

  3. 第二,在图片上应用极少量的模糊,方法是将每个像素及其每个位置的8个周围像素取平均值:-blur 1x65535(此操作使用3x3平方内核)。在该步骤之后,在得到的图像中,仅那些像素将保持纯黑色,其仅被原始图像中的黑色像素包围。

    第三次,将所有非黑色像素设为白色:对图像应用 -fill white +opaque black -fill white -opaque black - 操作。 (另请参阅"Morphology",特别是"Erosion"。)通过将所有非黑色颜色设置为白色,简化了对纯黑色像素的搜索,从图像中删除了所有其他颜色。 (注意:这对于这样的src.png文件不起作用,这些文件不包含至少一个带有纯黑色像素的3x3像素区域......

    第四:我们必须考虑图像边框上的像素(这些像素没有8个邻居!),因此我们使用{为它们指定颜色'none' {1}}。

    我将使用名为“logo:”的ImageMagick内置特殊图片来演示我的方法:

    -virtual-pixel none

    正如您可以很容易看到的,此图像以白色为主导颜色。 (所以我切换这个例子的代码,使所有白色像素变黑......)

    到目前为止

    命令行:

    convert logo: logo.png
    

    以下是两张并排的图片:

    • logo.png左侧
    • 右边的
    • 2_blur-log-virtpixnone.png

    第五: 皮革,冲洗,重复。

    现在让我们再应用这个算法的几次迭代,比如100,500,1000和1300,并且还允许对结果应用注释,这样我们就知道哪个图像是哪个:

    convert                                                \
       logo:                                               \
      -virtual-pixel none                                  \
       $(for i in {1..2}; do echo " -blur 1x65535 "; done) \
      -fill black                                          \
      -opaque white                                        \
       2_blur-logo-virtpixnone.png
    

    正如你可以清楚地看到的那样,我的算法使得黑色区域朝向那个位置收敛,当你查看原始for j in 100 500 1000 1300; do convert \ logo: \ -virtual-pixel none \ $(for i in $(seq 1 ${j}); do echo " -blur 1x65535 "; done) \ -fill black \ -opaque white \ -gravity NorthEast -annotate +10+10 "$j iterations" \ ${j}_blur-logo-virtpixnone.png done 时,你已经直观地猜测它是白色区域的“中心”:

    一旦你到达没有留下黑点的输出图像,你就经常迭代一次。返回一次迭代。 :-)

    现在应该只有非常有限数量的候选像素符合您的标准。

答案 1 :(得分:1)

我喜欢这种使用欧几里德距离形态学的方法 - 它让向导看起来很恶魔(!)并在一秒钟内运行!

convert logo: -virtual-pixel none             \
              -morphology Distance Euclidean  \
              -auto-level -channel GB         \
              -threshold 85% 85.png

enter image description here

并且95%和99%的阈值可以获得这些:

enter image description here

enter image description here

而且,如果你想要文本坐标:

convert logo: -virtual-pixel none -morphology Distance Euclidean -auto-level -threshold 99% txt:- | grep white
151,328: (255,255,255,1)  #FFFFFF  white
152,328: (255,255,255,1)  #FFFFFF  white

答案 2 :(得分:0)

这只是一种预感,而不是一个完整的答案,但您可以先使用模糊滤镜预处理图像。然后,模糊图像中黑色的像素必须在原始图像中具有黑色邻域。

但这对任意颜色都不起作用。

答案 3 :(得分:0)

这是一个不同的想法,只需几分之一秒......

如果您正在寻找大的黑色区域,请反复将图像平铺为越来越小的图块,直到您获得完全黑色的图块,然后选择该图块的中心。这是一个显示收敛的小视频。该过程正在查看半透明的红色区域,直到发现只包含黑色的区域。

enter image description here

以下是代码:

#!/bin/bash
# Set interesting pixels to black, others to white
convert logo: -fill black +opaque white -negate start.png

# Now tile, into 4,9,16,25 till we get a tile that is fully black
for i in $(seq 2 8); do
   rm tmp*.png 2> /dev/null
   convert -crop ${i}x${i}@ start.png tmp%d.png
   for f in tmp*png; do
     mean=$(identify -format "%[mean]" "$f")
     if [ "$mean" = "0" ]; then
        j=$((i*i))
        echo "$f" of $j tiles
        exit
     fi
   done
done

输出是这样的:

tmp6.png of 9 tiles