Page 1 of 2

How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T03:50:54-07:00
by konstantin
I would like to assemble an ImageMagick command line which convert an image to pure black and white colors by simply thresholding it:

Code: Select all

convert rose: -colorspace gray  -ordered-dither threshold -colors 2 output.gif
After issuing:

Code: Select all

identify -verbose output.gif

Code: Select all

 Histogram:
      2640: (  0,  0,  0) #000000 gray(0)
       580: (255,255,255) #FFFFFF gray(255)
And I would like to get nearly same amount of black and white pixels. I will apply this filter to video frames, because I want to achieve a similar effect like in the older James Bond movies, but in black and white.

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T04:33:56-07:00
by snibgo
Doing it without dither is easy:

Code: Select all

convert rose: -colorspace gray  -contrast-stretch 50%,50% o.png

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T05:08:32-07:00
by konstantin
Thx, but this doesn't work with jpg image. My input image currently is:

Image

And I am using a Ruby script and a simple binary search algorithm ( https://en.wikipedia.org/wiki/Bisection_method )to find a gamma value, which can be pre-applied to the input image to achieve my goal:

Code: Select all

#!/usr/bin/ruby

def convert(input,output,gamma)
 %x{convert "#{input}" -gamma #{gamma.round(8).to_s} -colorspace gray  -ordered-dither threshold -colors 2 #{output}}
 ret=%x{identify -verbose #{output}}
 md=/Histogram:\n +([0-9]+):.*?\n +([0-9]+):.*?\n/.match(ret)[1..2].map{|z| z.to_f}
 difference=(50.0-100.0*md[0]/(md[0]+md[1])).abs
 return [difference,md]
end

input=ARGV[0]
allowed_difference=0.5 #percent
output="output.gif"

gamma=1.0

difference,md=convert(input,output,gamma)

if md[0]>md[1] then 
  gamma_upper=8.0
  gamma_lower=gamma
else
  gamma_upper=gamma
  gamma_lower=0.0
end

step=0

while step<=40 && difference>allowed_difference do
  gamma=(gamma_upper+gamma_lower)/2
  difference,md=convert(input,output,gamma)
  if md[0]>md[1] then 
    gamma_lower=gamma
  else
    gamma_upper=gamma
  end
   
  step+=1
  local_variables.each{|z| puts "#{z} -> #{eval(z.to_s)}"}
  puts "--------------------------------------------------------"
  
end

puts difference
puts gamma
When running the program:

Code: Select all

allowed_difference -> 0.5
output -> output.gif
gamma -> 4.5
difference -> 30.022222222222222
md -> [40455.0, 162045.0]
gamma_upper -> 4.5
gamma_lower -> 1.0
step -> 1
--------------------------------------------------------
input -> hatter2.jpg
allowed_difference -> 0.5
output -> output.gif
gamma -> 2.75
difference -> 18.232592592592592
md -> [64329.0, 138171.0]
gamma_upper -> 2.75
gamma_lower -> 1.0
step -> 2
--------------------------------------------------------
input -> hatter2.jpg
allowed_difference -> 0.5
output -> output.gif
gamma -> 1.875
difference -> 5.959012345679014
md -> [89183.0, 113317.0]
gamma_upper -> 1.875
gamma_lower -> 1.0
step -> 3
--------------------------------------------------------
input -> hatter2.jpg
allowed_difference -> 0.5
output -> output.gif
gamma -> 1.4375
difference -> 23.161481481481488
md -> [148152.0, 54348.0]
gamma_upper -> 1.875
gamma_lower -> 1.4375
step -> 4
--------------------------------------------------------
input -> hatter2.jpg
allowed_difference -> 0.5
output -> output.gif
gamma -> 1.65625
difference -> 5.038518518518515
md -> [111453.0, 91047.0]
gamma_upper -> 1.875
gamma_lower -> 1.65625
step -> 5
--------------------------------------------------------
input -> hatter2.jpg
allowed_difference -> 0.5
output -> output.gif
gamma -> 1.765625
difference -> 1.4350617283950626
md -> [98344.0, 104156.0]
gamma_upper -> 1.765625
gamma_lower -> 1.65625
step -> 6
--------------------------------------------------------
input -> hatter2.jpg
allowed_difference -> 0.5
output -> output.gif
gamma -> 1.7109375
difference -> 1.5930864197530852
md -> [104476.0, 98024.0]
gamma_upper -> 1.765625
gamma_lower -> 1.7109375
step -> 7
--------------------------------------------------------
input -> hatter2.jpg
allowed_difference -> 0.5
output -> output.gif
gamma -> 1.73828125
difference -> 0.016790123456793538
md -> [101216.0, 101284.0]
gamma_upper -> 1.73828125
gamma_lower -> 1.7109375
step -> 8
--------------------------------------------------------
0.016790123456793538
1.73828125

Of course it is slow, the resulting image is:

Image

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T05:33:30-07:00
by snibgo
That's complex. It would be simpler to get the histogram of the image, find the 50% mark, and threshold at that level.

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T05:56:12-07:00
by konstantin
I see, the problem is that I have little knowledge in image processing and ImageMagick, but I can easily code in Ruby. For example for this image (after converting to gray colorspace) I get the following histogram:

Code: Select all

 Histogram:
         1: (  1,  1,  1) #010101 gray(1)
        18: (  2,  2,  2) #020202 gray(2)
        88: (  3,  3,  3) #030303 gray(3)
       284: (  4,  4,  4) #040404 gray(4)
       559: (  5,  5,  5) #050505 gray(5)
      1203: (  6,  6,  6) #060606 gray(6)
      1549: (  7,  7,  7) #070707 gray(7)
      2151: (  8,  8,  8) #080808 gray(8)
      3049: (  9,  9,  9) #090909 gray(9)
      3791: ( 10, 10, 10) #0A0A0A gray(10)
      3817: ( 11, 11, 11) #0B0B0B gray(11)
      4073: ( 12, 12, 12) #0C0C0C gray(12)
      3930: ( 13, 13, 13) #0D0D0D gray(13)
      3572: ( 14, 14, 14) #0E0E0E gray(14)
      3078: ( 15, 15, 15) #0F0F0F gray(15)
      2763: ( 16, 16, 16) #101010 gray(16)
      2309: ( 17, 17, 17) #111111 gray(17)
      1993: ( 18, 18, 18) #121212 gray(18)
      1775: ( 19, 19, 19) #131313 gray(19)
      1755: ( 20, 20, 20) #141414 gray(20)
      1566: ( 21, 21, 21) #151515 gray(21)
      1478: ( 22, 22, 22) #161616 gray(22)
      1421: ( 23, 23, 23) #171717 gray(23)
      1388: ( 24, 24, 24) #181818 gray(24)
      1399: ( 25, 25, 25) #191919 gray(25)
      1326: ( 26, 26, 26) #1A1A1A gray(26)
      1300: ( 27, 27, 27) #1B1B1B gray(27)
      1365: ( 28, 28, 28) #1C1C1C gray(28)
      1201: ( 29, 29, 29) #1D1D1D gray(29)
      1139: ( 30, 30, 30) #1E1E1E gray(30)
      1135: ( 31, 31, 31) #1F1F1F gray(31)
       929: ( 32, 32, 32) #202020 gray(32)
       866: ( 33, 33, 33) #212121 gray(33)
       748: ( 34, 34, 34) #222222 gray(34)
       710: ( 35, 35, 35) #232323 gray(35)
       698: ( 36, 36, 36) #242424 gray(36)
       656: ( 37, 37, 37) #252525 gray(37)
       664: ( 38, 38, 38) #262626 gray(38)
       638: ( 39, 39, 39) #272727 gray(39)
       635: ( 40, 40, 40) #282828 gray(40)
       645: ( 41, 41, 41) #292929 gray(41)
       666: ( 42, 42, 42) #2A2A2A gray(42)
       678: ( 43, 43, 43) #2B2B2B gray(43)
       658: ( 44, 44, 44) #2C2C2C gray(44)
       658: ( 45, 45, 45) #2D2D2D gray(45)
       658: ( 46, 46, 46) #2E2E2E gray(46)
       638: ( 47, 47, 47) #2F2F2F gray(47)
       685: ( 48, 48, 48) #303030 gray(48)
       669: ( 49, 49, 49) #313131 gray(49)
       670: ( 50, 50, 50) #323232 gray(50)
       737: ( 51, 51, 51) #333333 gray(51)
       685: ( 52, 52, 52) #343434 gray(52)
       745: ( 53, 53, 53) #353535 gray(53)
       665: ( 54, 54, 54) #363636 gray(54)
       718: ( 55, 55, 55) #373737 gray(55)
       766: ( 56, 56, 56) #383838 gray(56)
       753: ( 57, 57, 57) #393939 gray(57)
       764: ( 58, 58, 58) #3A3A3A gray(58)
       759: ( 59, 59, 59) #3B3B3B gray(59)
       781: ( 60, 60, 60) #3C3C3C gray(60)
       825: ( 61, 61, 61) #3D3D3D gray(61)
       871: ( 62, 62, 62) #3E3E3E gray(62)
       862: ( 63, 63, 63) #3F3F3F gray(63)
       865: ( 64, 64, 64) #404040 gray(64)
       991: ( 65, 65, 65) #414141 gray(65)
       944: ( 66, 66, 66) #424242 gray(66)
      1072: ( 67, 67, 67) #434343 gray(67)
      1044: ( 68, 68, 68) #444444 gray(68)
      1137: ( 69, 69, 69) #454545 gray(69)
      1214: ( 70, 70, 70) #464646 gray(70)
      1244: ( 71, 71, 71) #474747 gray(71)
      1392: ( 72, 72, 72) #484848 gray(72)
      1509: ( 73, 73, 73) #494949 gray(73)
      1606: ( 74, 74, 74) #4A4A4A gray(74)
      1727: ( 75, 75, 75) #4B4B4B gray(75)
      1891: ( 76, 76, 76) #4C4C4C gray(76)
      1918: ( 77, 77, 77) #4D4D4D gray(77)
      2002: ( 78, 78, 78) #4E4E4E gray(78)
      2310: ( 79, 79, 79) #4F4F4F gray(79)
      2371: ( 80, 80, 80) #505050 gray(80)
      2464: ( 81, 81, 81) #515151 gray(81)
      2505: ( 82, 82, 82) #525252 gray(82)
      2633: ( 83, 83, 83) #535353 gray(83)
      2748: ( 84, 84, 84) #545454 gray(84)
      3012: ( 85, 85, 85) #555555 gray(85)
      3022: ( 86, 86, 86) #565656 gray(86)
      3073: ( 87, 87, 87) #575757 gray(87)
      2989: ( 88, 88, 88) #585858 gray(88)
      2909: ( 89, 89, 89) #595959 gray(89)
      3203: ( 90, 90, 90) #5A5A5A gray(90)
      3200: ( 91, 91, 91) #5B5B5B gray(91)
      3187: ( 92, 92, 92) #5C5C5C gray(92)
      3085: ( 93, 93, 93) #5D5D5D gray(93)
      2815: ( 94, 94, 94) #5E5E5E gray(94)
      2771: ( 95, 95, 95) #5F5F5F gray(95)
      2658: ( 96, 96, 96) #606060 gray(96)
      2468: ( 97, 97, 97) #616161 gray(97)
      2540: ( 98, 98, 98) #626262 gray(98)
      2445: ( 99, 99, 99) #636363 gray(99)
      2440: (100,100,100) #646464 gray(100)
      2370: (101,101,101) #656565 gray(101)
      2245: (102,102,102) #666666 gray(102)
      2117: (103,103,103) #676767 gray(103)
      1952: (104,104,104) #686868 gray(104)
      1853: (105,105,105) #696969 gray(105)
      1678: (106,106,106) #6A6A6A gray(106)
      1527: (107,107,107) #6B6B6B gray(107)
      1443: (108,108,108) #6C6C6C gray(108)
      1429: (109,109,109) #6D6D6D gray(109)
      1417: (110,110,110) #6E6E6E gray(110)
      1422: (111,111,111) #6F6F6F gray(111)
      1368: (112,112,112) #707070 gray(112)
      1245: (113,113,113) #717171 gray(113)
      1180: (114,114,114) #727272 gray(114)
      1115: (115,115,115) #737373 gray(115)
      1011: (116,116,116) #747474 gray(116)
       941: (117,117,117) #757575 gray(117)
       878: (118,118,118) #767676 gray(118)
       819: (119,119,119) #777777 gray(119)
       826: (120,120,120) #787878 gray(120)
       674: (121,121,121) #797979 gray(121)
       671: (122,122,122) #7A7A7A gray(122)
       582: (123,123,123) #7B7B7B gray(123)
       530: (124,124,124) #7C7C7C gray(124)
       494: (125,125,125) #7D7D7D gray(125)
       428: (126,126,126) #7E7E7E gray(126)
       477: (127,127,127) #7F7F7F gray(127)
       463: (128,128,128) #808080 gray(128)
       455: (129,129,129) #818181 gray(129)
       404: (130,130,130) #828282 gray(130)
       391: (131,131,131) #838383 gray(131)
       385: (132,132,132) #848484 gray(132)
       339: (133,133,133) #858585 gray(133)
       359: (134,134,134) #868686 gray(134)
       341: (135,135,135) #878787 gray(135)
       331: (136,136,136) #888888 gray(136)
       310: (137,137,137) #898989 gray(137)
       333: (138,138,138) #8A8A8A gray(138)
       338: (139,139,139) #8B8B8B gray(139)
       333: (140,140,140) #8C8C8C gray(140)
       354: (141,141,141) #8D8D8D gray(141)
       371: (142,142,142) #8E8E8E gray(142)
       353: (143,143,143) #8F8F8F gray(143)
       324: (144,144,144) #909090 gray(144)
       338: (145,145,145) #919191 gray(145)
       322: (146,146,146) #929292 gray(146)
       295: (147,147,147) #939393 gray(147)
       261: (148,148,148) #949494 gray(148)
       229: (149,149,149) #959595 gray(149)
       244: (150,150,150) #969696 gray(150)
       214: (151,151,151) #979797 gray(151)
       175: (152,152,152) #989898 gray(152)
       155: (153,153,153) #999999 gray(153)
       119: (154,154,154) #9A9A9A gray(154)
        88: (155,155,155) #9B9B9B gray(155)
        67: (156,156,156) #9C9C9C gray(156)
        47: (157,157,157) #9D9D9D gray(157)
        25: (158,158,158) #9E9E9E gray(158)
        20: (159,159,159) #9F9F9F gray(159)
        17: (160,160,160) #A0A0A0 gray(160)
        10: (161,161,161) #A1A1A1 gray(161)
         9: (162,162,162) #A2A2A2 gray(162)
         4: (163,163,163) #A3A3A3 gray(163)
         2: (164,164,164) #A4A4A4 gray(164)
         1: (165,165,165) #A5A5A5 gray(165)
         1: (166,166,166) #A6A6A6 gray(166)
         1: (167,167,167) #A7A7A7 gray(167)

Easy to interpret the data with Ruby:

Code: Select all

input=ARGV[0];
%x{convert "#{input}" -colorspace gray output.bmp};
ret=%x{identify -verbose output.bmp};

#extract numbers of pixels at a given graylevel in the form [[2,3],[3,5],... [number_of_pixels,graylevel]]
hist=(/Histogram:\n(.*?)Rendering intent/m).match(ret)[1].scan(/([0-9]+):.*?gray\(([0-9]+)\)/).map{|z| z.map{|q| q.to_i}};

#number of pixels
n=hist.inject(0){|a,b| a+=b[0]};

i=0
m=0
while m<=n/2 do
  m+=hist[i][0]
  i+=1
end

#the needed graylevel
puts hist[i-1][1] 
Then how can I threshold at that identified graylevel (79)?

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T06:49:22-07:00
by snibgo
79 is 30.98% of 255. (79/255*100 = 30.98). So:

Code: Select all

-threshold 30.98%

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T06:54:32-07:00
by konstantin
Thx, it works, resulting image is very similar:
Image

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T07:08:19-07:00
by konstantin
And how can I get the histogram data from a jpg image grayscale equivalent without really converting it to a temporary image?

I mean something like:

Code: Select all

convert input.jpg -colorspace gray  -info:histogram
BTW one can get good results by setting the ratio of black and white pixels to the golden ratio 1/1.618033 or 1-1/1.618033 .

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T08:02:50-07:00
by snibgo
A quick method is:

Code: Select all

convert 7cXqWHe.jpg -colorspace gray -depth 8 -verbose info:

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T09:00:34-07:00
by konstantin
First result clip:
Image

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T09:14:30-07:00
by snibgo
Ha! Cool.

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T09:21:55-07:00
by konstantin
Yes, because I applied a slow motion effect too, to double the number of frames. Maybe when converting to gray colorspace for making such a B/W animation one should discard the chroma information of the image completely, am I right?

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T10:23:44-07:00
by snibgo
"-colorspace gray" does that. If you are using ffmpeg to extract frames from a video, converting to gray may be quicker using ffmpeg.

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T10:41:37-07:00
by konstantin
"stabilized version": determined golden-ratio graylevel is averaged with graylevel got from the previous frame:
Image

Re: How to threshold an image to get about same amount of black and white?

Posted: 2016-01-16T10:42:01-07:00
by fmw42
Both of these commands seem to work for me to match your image using IM 6.9.3.0 Q16 Mac OSX

Code: Select all

convert 7cXqWHe.jpg -colorspace gray -contrast-stretch 50,50% show:
convert 7cXqWHe.jpg -colorspace gray -linear-stretch 50,50% show:
It should be the same as looking at the cumulative histogram and getting the 50% value and thresholding on that.

Do I misunderstand what you are doing, since I do not see why you need to use the histogram directly?