1

I am wanting to get the average brightness of a file in python. Having read a previous question [Problem getting terminal output from ImageMagick's compare.exe ( Either by pipe or Python ) I have come up with :

cmd='/usr/bin/convert {} -format "%[fx:100*image.mean]\n" info: >    bright.txt'.format(full)
subprocess.call(cmd,shell=True)
with open('bright.txt', 'r') as myfile:
    x=myfile.read().replace('\n', '')
return x

the previous question recommended the use of 'pythonmagick' which I can find but with no current documentation and very little recent activity. I could not work out the syntax to use it.

I know that my code is unsatisfactory but it does work. Is there a better way which does not need 'shell=true' or additional file processing ?

jww
  • 97,681
  • 90
  • 411
  • 885
Iain1940
  • 29
  • 5
  • I am not an expert on Python Wand, but you can do fx commands in Python Wand. See http://docs.wand-py.org/en/0.4.1/wand/image.html – fmw42 Sep 17 '18 at 18:27
  • You can also use Python Pillow to get the mean of an image. See https://pillow.readthedocs.io/en/5.2.x/reference/ImageStat.html – fmw42 Sep 17 '18 at 18:53

2 Answers2

1

This seems to works for me to return the mean as a variable that can be printed. (This is a bit erroneous. See the correction near the bottom)

#!/opt/local/bin/python3.6

import subprocess

cmd = '/usr/local/bin/convert lena.jpg -format "%[fx:100*mean]" info:'

mean=subprocess.call(cmd, shell=True)
print (mean)


The result is 70.67860, which is returned to the terminal.

This also works with shell=False, if you parse each part of the command.

#!/opt/local/bin/python3.6

import subprocess

cmd = ['/usr/local/bin/convert','lena.jpg','-format','%[fx:100*mean]','info:']

mean=subprocess.call(cmd, shell=False)
print (mean)


The result is 70.67860, which is returned to the terminal.

The comment from tripleee below indicates that my process above is not correct in that the mean is being shown at the terminal, but not actually put into the variable.

He suggested to use subprocess.check_output(). The following is his solution. (Thank you, tripleee)

#!/opt/local/bin/python3.6

import subprocess

filename = 'lena.jpg'
mean=subprocess.check_output(
    ['/usr/local/bin/convert',
     filename,
     '-format',
     'mean=%[fx:100*mean]', 
     'info:'], universal_newlines=True)
print (mean)

Prints: mean=70.6786

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • 1
    This doesn't capture the output in `mean`, it just gets printed on standard output *before* you try to `print` the value from Python. You want `subprocess.check_output()` or its modern replacement `subprocess.run()` instead of `subprocess.call()`. – tripleee Sep 18 '18 at 05:46
  • `@tripleee`. Thanks for the correction. I am a novice with Python. But I have posted a correction in my post with your solution. – fmw42 Sep 18 '18 at 16:14
  • Add `universal_newlines=True` to receive the output as a decoded string. The OS performs I/O on `bytes` buffers in Python 3 by default. (Alternatively, `decode` the `bytes` object separately; but that's a hassle because then you need to specify the correct encoding.) – tripleee Sep 18 '18 at 16:19
  • The `b` and the quotes are not part of the value, they are just Python's annotations to disambiguate the `repr` version. – tripleee Sep 18 '18 at 16:21
  • I am not sure what you mean by `repr version`. But how would I take the returned variable and use that in some further calculations when it has the b and single quotes? – fmw42 Sep 18 '18 at 16:35
  • `decoded = b'foo'.decode('ascii')` gets you a string from a byte string. The `b'...'` decoration is just Python's annotation for a byte string; the actual string is the stuff between the quotes. Compare `u'unicode string'`, `r'raw string'`, etc. This is a common FAQ; see e.g. https://stackoverflow.com/questions/41918836/how-do-i-get-rid-of-the-b-prefix-in-a-string-in-python and/or https://stackoverflow.com/questions/1436703/difference-between-str-and-repr – tripleee Sep 18 '18 at 17:23
  • `@tripleee`. Thanks I learned something new today. That solved it. – fmw42 Sep 18 '18 at 18:38
0

You can probably improve the subprocess, and eliminate the temporary text file with Popen + PIPE.

cmd=['/usr/bin/convert',
     full,
     '-format',
     '%[fx:100*image.mean]',
     'info:']
pid = subprocess.Popen(cmd,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)
out, err = pid.communicate()
return float(out)

ImageMagick also ships with the identify utility. The same method could be achieved with...

cmd=['/usr/bin/identify', '-format', '%[fx:100*image.mean]', full]

It might be worth exploring if it's worth working directly with ImageMagick's shared libraries. Usually connected through C-API (pythonmagick, wand, &etc). For what your doing, this would only increase code-complexity, increase module dependancies, but in no way improve performance or accuracy.

emcconville
  • 23,800
  • 4
  • 50
  • 66
  • There is no good reason to reimplement `subprocess.check_output()` or its modern replacement `subprocess.run()`. Generally avoid `subprocess.Popen()` unless you really know what you are doing. – tripleee Sep 18 '18 at 05:43