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

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post 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.
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

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

Post by snibgo »

Doing it without dither is easy:

Code: Select all

convert rose: -colorspace gray  -contrast-stretch 50%,50% o.png
snibgo's IM pages: im.snibgo.com
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post 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
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

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

Post 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.
snibgo's IM pages: im.snibgo.com
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post 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)?
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

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

Post by snibgo »

79 is 30.98% of 255. (79/255*100 = 30.98). So:

Code: Select all

-threshold 30.98%
snibgo's IM pages: im.snibgo.com
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post by konstantin »

Thx, it works, resulting image is very similar:
Image
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post 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 .
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

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

Post by snibgo »

A quick method is:

Code: Select all

convert 7cXqWHe.jpg -colorspace gray -depth 8 -verbose info:
snibgo's IM pages: im.snibgo.com
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post by konstantin »

First result clip:
Image
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

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

Post by snibgo »

Ha! Cool.
snibgo's IM pages: im.snibgo.com
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post 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?
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

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

Post 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.
snibgo's IM pages: im.snibgo.com
konstantin
Posts: 50
Joined: 2013-08-07T13:50:31-07:00
Authentication code: 6789

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

Post by konstantin »

"stabilized version": determined golden-ratio graylevel is averaged with graylevel got from the previous frame:
Image
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

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

Post 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?
Post Reply