Page 1 of 1

Create histogram image from multiple images

Posted: 2016-12-18T20:45:35-07:00
by MilesS
Help please,

I have a series of images, I want a combined histogram of all images.
Each column = seperate image and row = color (0-255) and Intensity = histogram percentage

i.e.
Get the histogram for each image as a 1xN(color) where the intensity = 255 x fraction of total counts
Append this into one large histogram, each column is from a new file.

This almost works

Code: Select all

convert *.png -colors 255 -unique-colors -rotate 90 +append hist.png
https://drive.google.com/file/d/0B13mzT ... sp=sharing
However, the colors move (vertically) and I don't have the counts.

I think a partial solution is viewtopic.php?f=1&t=19538&p=76915#p76915

Code: Select all

convert rose: -colors 256 -format %c histogram:info:- | sed 's/:.*#/ #/' | 
  while read count color colorname; do
     convert -size 1x$count xc:$color miff:-
  done |
    convert - -alpha set -gravity south -background none +append  unique_color_histogram.png
I'm getting lost here but I think I need to change the second convert to

Code: Select all

 convert -size 1x1 xc: $color * $count / $TotalPixels miff:-
And I need to sort by color (1 to 256) not by counts

Re: Create histogram image from multiple images

Posted: 2016-12-18T21:40:05-07:00
by snibgo
I don't understand what you want. Making a histogram from all the pixels in a number of images, as if it was just one image, is easy. (Chop all the images into lines, and append the results horizontally.) But I don't think that's what you want.

You want exactly one output column per input image, is that right?

Fred has bash scripts that may be useful at http://www.fmwconcepts.com/imagemagick/histog/index.php etc.

I have some Windows BAT scripts, and also process modules at my "Process modules" page.

Re: Create histogram image from multiple images

Posted: 2016-12-18T22:23:22-07:00
by fmw42
I do not understand what you want. Can you explain a bit further and provide a diagram of what you expect? What is the purpose of such a histogram?

Is this a 2D histogram where each row represents a fraction (probability) between 0 and 1 as the first column and every other column is the color from a given image? If so, you cannot guarantee to have consistent fraction values (first column) for each image. That would require a second level of binning?

Re: Create histogram image from multiple images

Posted: 2016-12-19T01:41:39-07:00
by MilesS
Sorry, it wasn't clear.
I have about 10000 time-scale images that I want to quickly compare histograms between each image in a 3D histogram?

for instance with four images and eight colours I could use
convert image-1.png -depth 1 -format %c histogram:info:- > image-1.txt

and matching colours generate this table
Color Image-1 Image-2 Image-3 Image-4
blue 220437 218769 217361 214422
black 149422 149440 150239 150557
cyan 103968 102423 99608 95234
white 53471 52137 49599 46876
magenta 42875 45194 47559 49348
red 36895 38113 39579 43491
lime 21404 21728 22086 23558
yellow 3129 3797 5570 8115

Using this table generate a 4x8 pixel image. where row 1 is always black, column 1 is always image-1 and intensity (gamma) reflects the value, scaled between each row's minimum and maximum
i.e. for each row, gamma is (value - min)/(max - min)

Re: Create histogram image from multiple images

Posted: 2016-12-19T11:21:31-07:00
by fmw42
-depth 1 is not going to give you those colors. It will be binary (black and white). You will need to set up a color table image and use -remap to force those exact colors. (See http://www.imagemagick.org/Usage/quantize/#remap). Once you do that, you can generate a histogram as text and as a row. Then stack the rows for each image and convert that to a raster, then transpose the raster to get your column format.

Or you can use your OS to sort the histogram and transpose to column format.

If you make the first row black, then you have 9 rows, not 8.

Sorry I do not understand the connection between the data and your intensity/gamma discussion. The values in your table are counts not graylevels.

What do you intend to do with such a histogram? Why is it 3D? I only see 2D from your discussion.

What is your IM version and Platform? Please always supply that information, since syntax may differ.

Re: Create histogram image from multiple images

Posted: 2016-12-19T16:08:49-07:00
by MilesS
ImageMagick 6.8.9-9 Q16 x86_64 2016-11-29
OS: Bash on Ubuntu on Windows (Microsoft's Windows 10's new Linux interface see http://www.howtogeek.com/265900/everyth ... ash-shell/ )

ok agreed colors is probably better but depth is faster. Once it's working will increase the colors to 256.
milesS@W541:~$ convert rose: -depth 1 -format %c histogram:info:-
1343: ( 0, 0, 0) #000000 black
41: ( 0, 0,255) #0000FF blue
170: ( 0,255, 0) #00FF00 lime
7: ( 0,255,255) #00FFFF cyan
1249: (255, 0, 0) #FF0000 red
20: (255, 0,255) #FF00FF magenta
41: (255,255, 0) #FFFF00 yellow
349: (255,255,255) #FFFFFF white

Yes, including black gives 9.
2D/3D, sorry we are talking across different areas of expertise. It's multidimensional data displayed as a 2D image

Ok that's the easy questions out the way :-)
My images are a series of satellite images every ten minutes. I'm then doing a series of image differences, Image(now) and Image(-1 hour), using your script viewtopic.php?t=15584#p55272. I want to know what changed and when? If I look in the visible spectrum, for instance, white are clouds, blue/black are thunderstorms. So if I plot different images (10-minute timesteps) as columns and histograms vertically I get the percentage cloud/thunderstorm in an easy to read single image. I want to retain the color in the vertical but percentages must be easily discernable as well. In my example, I would like to see how the clouds change from predominantly white to black and what time duration this occurred and how often this occurred. Looking at the multi-image histogram I may notice peaks of color developing e.g. the changing yellow of my earlier data, this could be indicative of a dust storm. I could then get the column, convert this to an image date and then look at that specific image to determine the magnitude and location of the dust storm.

I'm thinking that I may have to put two vertical bars on the left and right edge showing the color and a grayscale middle section as it is important to observe if the color starts changing e.g. blue to black. If I'm simultaneously changing intensity and color than those subtle differences may get lost.

Re: Create histogram image from multiple images

Posted: 2016-12-19T16:22:33-07:00
by snibgo
I think you want to reduce each input 2D image to one dimension. Is that right? A column with no width. (Or, rather, a width of one pixel.)

You could simply rearrange the pixels into a single column, with the darkest pixels at the bottom, the lightest at the top. This would be a very tall image, so you would probably resize it to be just 500 pixels high or whatever you want. (This isn't really a histogram.)

Do the same for all your satellite images, and append them side by side.

How does that sound?

Re: Create histogram image from multiple images

Posted: 2016-12-19T21:48:05-07:00
by fmw42
I have something that works for most images.

Code: Select all

colors=`convert rose: -depth 1 -format "%c" histogram:info: | sed -n 's/^.*#[^ ]* \(.*\)$/\1/p' | tr "\n" " "`
echo "$colors"
black blue lime cyan red magenta yellow white

Code: Select all

colors=`convert logo: -depth 1 -format "%c" histogram:info: | sed -n 's/^.*#[^ ]* \(.*\)$/\1/p' | tr "\n" " "`
echo "$colors"
black blue lime cyan red yellow white

Code: Select all

imgArr=(rose: logo: netscape: wizard:)
( for ((i=0; i<4; i++)); do
OLDIFS=$IFS
IFS=$'\n'
data=`convert "${imgArr[$i]}" -depth 1 -format "%c" histogram:info: | sed -n 's/^ *\(.*\):.*$/\1/p' | tr "\n" " "`
IFS=$OLDIFS
#echo >&2 "$data"
max=`echo "$data" | tr " " "\n" | sort -n -r -k1 | head -n 1`
#echo >&2 "$max"
newdata=""
for val in $data; do
val=`convert xc: -format "%[fx:round($val*65535/$max)]" info:`
newdata="$newdata $val"
done
#echo >&2 "$newdata"
#echo >&2 ""
echo "P2 8 1 65535 $newdata" | convert - -depth 16 miff:-
done ) | convert - -append -transpose result.png

But the problem is that IM histograms, do not give bins for which there are no counts. So in the case of logo:, your -depth 1 histogram command only gives 7 colors and is missing magenta. So I will have to modify the above to test for the existence of each possible 8 colors and add an entry of count=0 for any missing colors.

Re: Create histogram image from multiple images

Posted: 2016-12-20T11:39:47-07:00
by fmw42
I think this does what you want.

Code: Select all

imgArr=(rose: logo: netscape: wizard:)
numimgs=${#imgArr[*]}
( for ((i=0; i<numimgs; i++)); do
OLDIFS=$IFS
IFS=$'\n'
hist=`convert "${imgArr[$i]}" -depth 1 -format "%c" histogram:info:`
counts=`echo "$hist" | sed -n 's/^ *\(.*\):.*$/\1/p' | tr "\n" " " | sed 's/[ ]*$//'`
colors=`echo "$hist" | sed -n 's/^.*#[^ ]* \(.*\)$/\1/p' | tr "\n" " " | sed 's/[ ]*$//'`
IFS=$OLDIFS
#echo >&2 "$counts"
#echo >&2 "$colors"
max=`echo "$counts" | tr " " "\n" | sort -n -r -k1 | head -n 1`
min=`echo "$counts" | tr " " "\n" | sort -n -k1 | head -n 1`
mmm=$((max-min))
[ $mmm -eq 0 ] && mmm=1
#echo >&2 "max=$max"
#echo >&2 "min=$min"
countsArr=($counts)
colorsArr=($colors)
numcolors=${#colorsArr[*]}
#echo >&2 "numcolors=$numcolors"
newcountArr=(0 0 0 0 0 0 0 0)
for ((j=0; j<numcolors; j++)); do
if [ "${colorsArr[$j]}" = "black" ]; then
     newcountArr[0]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
elif [ "${colorsArr[$j]}" = "blue" ]; then
     newcountArr[1]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
elif [ "${colorsArr[$j]}" = "lime" ]; then
     newcountArr[2]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
elif [ "${colorsArr[$j]}" = "cyan" ]; then
     newcountArr[3]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
elif [ "${colorsArr[$j]}" = "red" ]; then
     newcountArr[4]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
elif [ "${colorsArr[$j]}" = "magenta" ]; then
     newcountArr[5]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
elif [ "${colorsArr[$j]}" = "yellow" ]; then
     newcountArr[6]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
elif [ "${colorsArr[$j]}" = "white" ]; then
     newcountArr[white]=`echo "scale=0; 65535*(${countsArr[$j]}-$min)/$mmm" | bc`
fi
done
#echo >&2 "${newcountArr[*]}"
#echo >&2 ""
echo "P2 8 1 65535 ${newcountArr[*]}" | convert - -depth 16 miff:-
done ) | convert - -append -transpose result.png