2
\$\begingroup\$

I am trying to clean up and make the following code more efficient -

https://gist.github.com/eWizardII/6757364

from PIL import Image
import numpy as np
from PIL import ImageChops
import subprocess
import math
import datetime
from PIL import ImageFont
from PIL import ImageDraw
from collections import deque


def image_entropy(prev, curr, smasumm, values):
    im1 = Image.open(prev)
    im2 = Image.open(curr)
    img = ImageChops.difference(im1, im2)

    w, h = img.size
    a = np.array(img.convert('RGB')).reshape((w * h, 3))
    h, e = np.histogramdd(a, bins=(16,) * 3, range=((0, 256),) * 3)
    prob = h / np.sum(h) # normalize
    prob = prob[prob > 0] # remove zeros
    comp = -np.sum(prob * np.log2(prob))
    framsd = SD(values, smasumm)
    information = 'ENT: ' + str('{0:.2f}'.format(comp)) + ' SMA: ' + str('{0:.2f}'.format(smasumm)) + ' LB: ' + str(
        '{0:.2f}'.format(smasumm - framsd)) + ' UB: ' + str('{0:.2f}'.format(smasumm + framsd))
    cimg = Image.open(curr)
    draw = ImageDraw.Draw(cimg)
    font = ImageFont.truetype("arial.ttf", 24)
    draw.text((0, 0), information, (0, 255, 0), font=font)
    cimg.save(curr)
    return comp


def SD(values, mean):
    size = len(values)
    sumsd = 0.0
    for n in range(0, size):
        sumsd += math.sqrt((values[n] - mean) ** 2)
    return math.sqrt((1.0 / (size - 1)) * (sumsd / size))


try:

    print "Initial Image ..."

    time = datetime.datetime.now()
    filename = "image_"
    subprocess.call(
        "raspistill -t 1000 -ex night -awb auto -w 720 -h 480 -o %s" % filename + '1.jpg',
        shell=True)
    prev = filename + '1.jpg'
    i = 1
    summ = smasumm = 0.0
    period = n = 10
    values = deque([0.0] * period)
    while True:
        time = datetime.datetime.now()
        filename = "image_" + str(i) + ".jpg"
        subprocess.call(
            "raspistill -t 1000 -ex night -awb auto -w 720 -h 480 -o %s" % filename,
            shell=True)

        i += 1
        curr = filename
        sma = image_entropy(prev, curr, smasumm, values)
        values.append(sma)
        summ += sma - values.popleft()
        smasumm = summ / n
        prev = curr
        print(datetime.datetime.now() - time)
except KeyboardInterrupt:
    print "  Quit"

I believe my biggest bottleneck is in using the subprocess.call in Python but I don't know of any way to improve up such a call - in the long run when I know the program works properly I'll try and transition to Java/C; but this is easier for my immediate application and debugging. Any help or advice would be appreciated thanks in addition to any improvements in the other aspects of the code.

\$\endgroup\$
1
\$\begingroup\$

I don't know about "more efficient", but here are some hints and tips that will help you clean up the code and make it more readable:

Style remarks

  • My style checker asks for at least 2 spaces before an inline comment (lines 20-21). I don't think it's a bad idea.
  • According to PEP8, you should keep your lines shorter than 80 characters (lines 24, 25 and 49).
  • This line is not very clear.

    information = 'ENT: ' + str('{0:.2f}'.format(comp)) + ' SMA: ' + str('{0:.2f}'.format(smasumm)) + ' LB: ' + str(
     '{0:.2f}'.format(smasumm - framsd)) + ' UB: ' + str('{0:.2f}'.format(smasumm + framsd))
    

It would be a lot clearer to rewrite it like this:

'ENT: {0:.2f} SMA: {0:.2f} LB: {0:.2f} UB: {0:.2f}'.format(
    comp, smasumm, smasumm - framsd, smasumm + framsd)
  • Avoid one letter variable names (and other cryptic names like smasumm, because they're not descriptive and it's not immediately obvious what they represent.
  • The lack of comments makes it difficult to follow what you're trying to do. You should use docstrings to write a small paragraph that provides an explanation of what you're trying to do.
  • Again according to PEP8:

Imports should be grouped in the following order:

  • standard library imports

  • related third party imports

  • local application/library specific imports

So you want to replace your imports with this:

from collections import deque
import datetime
import math
import subprocess

import numpy as np
from PIL import Image
from PIL import ImageChops
from PIL import ImageDraw
from PIL import ImageFont
  • Why do you alias numpy as np? It's not very readable. I'd rather read numpy in full letters each time.
\$\endgroup\$
  • 1
    \$\begingroup\$ Re: aliasing numpy as np: this is the standard alias, and is used throughout the numpy source itself. It's also used by scipy, matplotlib, pandas, etc., pretty much everything on the stack. \$\endgroup\$ – DSM Sep 30 '13 at 20:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.