How to extract organized palette from image/video

2019-07-09 05:07发布

I use FFMPEG and Imagemagick to extract the color palette from an image or video with a Windows batch file,

:: get current folder name
for %%* in (.) do set CurrDirName=%%~nx*

:: get current filename
for /R %1 %%f in (.) do (
    set CurrFileName=%%~nf
)

ffmpeg -i "%1" -vf palettegen "_%CurrFileName%_temp_palette.png"
convert "_%CurrFileName%_temp_palette.png" -filter point -resize 4200%% "_%CurrFileName%_palette.png"

del "_%CurrFileName%_temp_palette.png"

This outputs something like,

Palette

I need this to have better transition throughout the color blocks though, like all blues from darkest to lightest then transitioning to greens, yellows etc. like,

Better Palette

Is there a way/switch to create this with either Imagemagick/FFMPEG?

1条回答
霸刀☆藐视天下
2楼-- · 2019-07-09 05:47

I don't like working on Windows, but I wanted to show you a technique you could use. I have therefore written it in bash but avoided nearly all Unix-y stuff and made it very simple. In order to run it on Windows, you would only need ImageMagick and awk and sort which you can get for Windows from here and here.

I'll demonstrate using an image of random data that the script creates on around the third line:

enter image description here

Here is the script. It is pretty well commented and should convert easily enough to Windows if you like what it does.

#!/bin/bash

# Create an initial image of random data 100x100
convert -size 100x100 xc:gray +noise random image.png

# Extract unique colours in image and convert to HSL colourspace and thence to text for "awk"
convert image.png -unique-colors -colorspace hsl -depth 8 txt: | awk -F"[(), ]+" '
!/^#/{
       H=$3; S=$4; L=$5            # Pick up HSL. For Hue, 32768=180deg, 65535=360deg. For S&L, 32768=50%, 65535=100%
       NGROUPS=4                   # Change this according to the number of groups of colours you want
       bin=65535/NGROUPS           # Calculate bin size
       group=int(int(H/bin)*bin)   # Split Hue into NGROUPS
       printf "%d %d %d %d\n",group,H,S,L
     }' > groupHSL.txt

# Sort by column 1 (group) then by column 4 (Lightness)
sort -n -k1,1 -k4,4 < groupHSL.txt > groupHSL-sorted.txt

# Reassemble the sorted pixels back into a simple image, 16-bit PNM format of HSL data
# Discard the group in column 1 ($1) that we used to sort the data
awk '  { H[++i]=$2; S[i]=$3; L[i]=$4 }
  END  {
           printf "P3\n"
           printf "%d %d\n",i,1
           printf "65535\n"
           for(j=1;j<=i;j++){
              printf "%d %d %d\n",H[j],S[j],L[j]
           }
        }' groupHSL-sorted.txt > HSL.pnm

# Convert HSL.pnm to sRGB.png
convert HSL.pnm -set colorspace hsl -colorspace sRGB sRGB.png

# Make squareish shape
convert sRGB.png -crop 1x1 miff:- | montage -geometry +0+0 -tile 40x - result.png

If I set NGROUPS to 10, I get:

enter image description here

If I set NGROUPS to 4, I get:

enter image description here

Note that, rather than using pipes and shell tricks, the script generates intermediate files so you can easily see each stage of the processing in order to debug it.

For example, if you run this:

convert image.png -unique-colors -colorspace hsl -depth 8 txt: | more

you will see the output that convert is passing to awk:

# ImageMagick pixel enumeration: 10000,1,255,hsl
0,0: (257,22359,1285)  #015705  hsl(1.41176,34.1176%,1.96078%)
1,0: (0,0,1542)  #000006  hsl(0,0%,2.35294%)
2,0: (41634,60652,1799)  #A2EC07  hsl(228.706,92.549%,2.7451%)
3,0: (40349,61166,1799)  #9DEE07  hsl(221.647,93.3333%,2.7451%)
4,0: (31868,49858,2056)  #7CC208  hsl(175.059,76.0784%,3.13725%)
5,0: (5140,41377,3341)  #14A10D  hsl(28.2353,63.1373%,5.09804%)
6,0: (61423,59367,3598)  #EFE70E  hsl(337.412,90.5882%,5.4902%)

If you look at groupHSL-sorted.txt, you will see how the pixels have been sorted into groups and then increasing lightness:

0 0 53456 10537
0 0 18504 20303
0 0 41377 24158
0 0 21331 25700
0 0 62708 28270
0 0 53199 31354
0 0 23130 32896
0 0 8738 33410
0 0 44204 36494
0 0 44204 36751
0 0 46260 38293
0 0 56283 40606
0 0 53456 45489
0 0 0 46517
0 0 32896 46517
0 0 16191 50372
0 0 49601 55769
0 257 49601 11565
0 257 42148 14392
0 257 53713 14649
0 257 50115 15677
0 257 48830 16191

Windows is particularly awkward at quoting things - especially scripts in single quotes like I use above for awk. I would suggest you extract the two separate awk scripts into separate files something like this:

script1.awk

!/^#/{
   H=$3; S=$4; L=$5            # Pick up HSL. For Hue, 32768=180deg, 65535=360deg. For S&L, 32768=50%, 65535=100%
   NGROUPS=4                   # Change this according to the number of groups of colours you want
   bin=65535/NGROUPS           # Calculate bin size
   group=int(int(H/bin)*bin)   # Split Hue into NGROUPS
   printf "%d %d %d %d\n",group,H,S,L
 }

and

script2.awk

{ H[++i]=$2; S[i]=$3; L[i]=$4 }
END  {
       printf "P3\n"
       printf "%d %d\n",i,1
       printf "65535\n"
       for(j=1;j<=i;j++){
          printf "%d %d %d\n",H[j],S[j],L[j]
       }
    }

Then the two lines in the main script will become something like:

convert image.png -unique-colors -colorspace hsl -depth 8 txt: | awk -F"[(), ]+" -f script1.awk > groupHSL.txt

and

awk -f script2.awk groupHSL-sorted.txt > HSL.pnm
查看更多
登录 后发表回答