Page 1 of 1

color reduction to specified color map and percentage of the image.

Posted: 2015-03-03T10:49:09-07:00
by whitehorse
Hello,

I've been experimenting with color reduction techniques. I have an image that I would like to reduce to around 16 colors without dithering so that the colors are relatively patchy across the image. I've had some success doing this.

But in addition I would like (1) to specify which 16 colors the image is reduced to and (2) to specify what percentage of the image each color will occupy. It doesn't matter if the colors are close the actual colors of the original image. It is these last two items that I am having difficulty. To give an over simplified example I would like to take an image and reduce it to certain colors (black, gray, white, red, etc) and then specify that black should only be 10% of the the pixels, or red should only be 20% of the pixels.

Maybe this is overly complicated. But if you have thoughts, please let me know.

Thanks,

Matthew

Re: color reduction to specified color map and percentage of the image.

Posted: 2015-03-03T20:06:01-07:00
by snibgo
Condition (1) is easy: use "-remap". See http://www.imagemagick.org/script/comma ... .php#remap

Condition (2) is harder. I can't immediately think of an easy way to do this. It might go something like this:

(A) Take the first of your required 16 colours (eg black, gray, red, etc). Make a difference between your input and a solid-colour image of the same size. Contrast-stretch and threshold so the required percentage (eg 10% or 20%, etc) become black and rest are white. This gives a mask that is black where the output should be the required colour, and white otherwise. From this mask and colour, make this partial image.

(B) Repeat (A) for all the required colours and percentages.

(C) Overlay the partial images.

(D) Due to rounding errors, you might get a few transparent pixels, so overlay the result of (C) on a solid-colour image using one of the colours.

Each single colour could be done in a single convert command.

Clearly, a loop is involved, so a script is required. For IM 6, you have a choice of looping through 16 convert commands, or building up one single long command in the loop, then executing it.

Someone else may think of a neater solution.

EDIT: My ABCD steps need another refinement because some pixels might qualify as being close two two or more of the required colours. We need to know which is the closest. So we also need to remap.

Re: color reduction to specified color map and percentage of the image.

Posted: 2015-03-04T10:41:54-07:00
by whitehorse
Thanks for all the help. This certainly sounds like a great plan. I am new to image manipulation and have been reading the help pages. I wonder if you might show me how to do this using a simple example.

Maybe if I could understand how to do this with three colors on another image then I could apply the same technique to my image with more colors. I have linked to an image of Seattle's skyline. What would I do if I wanted to apply this technique using three colors: black, gray, blue?

Image

Thanks!

Re: color reduction to specified color map and percentage of the image.

Posted: 2015-03-05T14:40:31-07:00
by snibgo
Given image INFILE and
colours COL1, COL2, ... COLn, COLFINAL, and
percentages PC1, PC2, ... PCn,
make an output image that has PC1% pixels COL1, PC2% pixels COL2, ... PCn% pixels COLn and all the rest are COLFINAL.

Strategy:

1. Find the PC1% of pixels that are closest to COL1.

2. Find the PC2% of pixels that were not found in (1) but are closest to COL2.

3. Repeat likewise up to N.

4. "-layers flatten" all the results, with background COLFINAL.

Here is a Windows BAT script. It is horrible. There may be a much better way.

Code: Select all

set COL1=Black
set COL2=Gray50
set COL3=Yellow
set COLFINAL=Blue

set PC1=20
set PC2=25
set PC3=30

set INFILE=seattle.jpg


%IM%convert ^
  %INFILE% ^
  ( +clone ^
    ( +clone -fill %COL1% -colorize 100 ) ^
    -compose Difference -composite ^
    -grayscale RMS ^
    +write m1.png +delete ^
  ) ^
  ( +clone ^
    ( +clone -fill %COL2% -colorize 100 ) ^
    -compose Difference -composite ^
    -grayscale RMS ^
    +write m2.png +delete ^
  ) ^
  ( +clone ^
    ( +clone -fill %COL3% -colorize 100 ) ^
    -compose Difference -composite ^
    -grayscale RMS ^
    +write m3.png +delete ^
  ) ^
  null:

%IM%convert ^
  m1.png ^
  -linear-stretch %PC1%%%,0 -fill White +opaque Black ^
  m1p.png

%IM%convert ^
  m2.png ^
  ( m1p.png -negate ) -compose Lighten -composite ^
  -linear-stretch %PC2%%%,0 -fill White +opaque Black ^
  m2p.png

%IM%convert ^
  m3.png ^
  ( m1p.png -negate ) -compose Lighten -composite ^
  ( m2p.png -negate ) -compose Lighten -composite ^
  -linear-stretch %PC3%%%,0 -fill White +opaque Black ^
  m3p.png

%IM%convert ^
  ( m1p.png -transparent White -fill %COL1% -opaque Black ) ^
  ( m2p.png -transparent White -fill %COL2% -opaque Black ) ^
  ( m3p.png -transparent White -fill %COL3% -opaque Black ) ^
  -background %COLFINAL% ^
  -layers flatten ^
  colpercents.png
All the converts could be combined into one.

The result, colpercents.png, is:
Image
The blocky sky results from JPEG compression of the input. I hate processing JPEGs.

I have calculated colour distances in RGB space. It might look better if in Lab or YIQ space.

Re: color reduction to specified color map and percentage of the image.

Posted: 2015-03-06T07:12:48-07:00
by whitehorse
Thanks for the help. It is a great solution for my purposes. I have another stupod question. I run Imagemagick most of the time in Linux, although I have access to a Windows machine. I tried to convert the script into a bash script however I think I am still missing some things. I know the heading needs to be changed for the tokens, the ^s need to be \s. Can you or anyone help with this? :D

Thanks,

Matthew

Re: color reduction to specified color map and percentage of the image.

Posted: 2015-03-06T07:50:14-07:00
by snibgo
In bash, you need to escape each parenthesis: \( and \).

Don't double up percentage signs %. So ...

Code: Select all

  -linear-stretch %PC2%%%,0 -fill White +opaque Black ^
... becomes something like ...

Code: Select all

  -linear-stretch $(PC2)%,0 -fill White +opaque Black \
If you put your bash script here, I can run it under Cygwin, and (try to) debug it. I'm not a bash expert.

Re: color reduction to specified color map and percentage of the image.

Posted: 2015-03-08T13:09:39-07:00
by snibgo
My algorithm and script above prioritises colours. If we specify colours in a different order, we'll get a different result.

I have thought of an algorithm that doesn't prioritise colours.

(1) Create an image called Prototype, the same size as the Input.

(2) Set pixels in Prototype, so that PC1 percent of the pixels are colour COL1, PC2% are COL2, etc.

(3) For any pair of pixels in Prototype that are different, would swapping them make Prototype closer to Input? If so, then swap the pixels.

(4) Repeat (3) for every possible pair of pixels.

I'm not sure, but (4) might have to be repeated until no more changes occur.

This is quite slow, O(p^2) where p is the number of pixels. But each step is very simple: we don't need to RMSE compare the entire Prototype to Image, merely the two pixels in question.

The algorithm might find a local minimum, rather than a global minimum. It might also get stuck in a repeated cycle of swaps. It could be coded as a process module. (It could be done as a script, but that would be very slow.)