Performance of the getOrientationAndMagnitude()
Published 10 February 2014I normally don’t do premature performance optimizations, and I was planning on optimizing the whole eye tracker later, when I felt I had all of the functionality I wanted, but from time to time, you still want to check your code, particularly when it’s suspiciously slow. So I did examine the code I had written so far using line_profiler (Abysmal documentation btw.) like so:
I have found that the most time is being spent in one function, getOrientationAndMagnitude
, you’ll recall it
looked like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def getOrientationAndMagnitude(image, show=False):
sobelHorizontal = cv2.Sobel(image, cv2.CV_32F, 1, 0)
sobelVertical = cv2.Sobel(image, cv2.CV_32F, 0, 1)
h = sobelHorizontal
v = sobelVertical
orientation = np.empty(image.shape)
magnitude = np.empty(image.shape)
height, width = h.shape
for y in range(height):
for x in range(width):
orientation[y][x] = cv2.fastAtan2(h[y][x], v[y][x])
magnitude = cv2.magnitude(h, v)
return orientation, magnitude
I wrote this code some time ago, when I was less familiar with OpenCV than I am now, and you can quickly see
the hotspot. Yes, the line 14
. I probably did it like this because I hadn’t noticed the
phase()
function OpenCV has.
But this will serve the purpose of showing how such a small oversight can have a dramatic effect on performance.
Armed with the phase()
function,
we can do the following:
1
2
3
4
5
6
7
8
def getOrientationAndMagnitude(image, show=False):
h = cv2.Sobel(image, cv2.CV_32F, 1, 0)
v = cv2.Sobel(image, cv2.CV_32F, 0, 1)
orientation = cv2.phase(h, v, angleInDegrees=True)
magnitude = cv2.magnitude(h, v)
return orientation, magnitude
Now this much shorter and simpler function will also run much faster, thanks to OpenCV. And here’s how I tested it:
1
2
3
4
5
6
7
8
import cProfile
image = cv2.imread('eye.png')
image = cv2.cvtColor(image, cv2.cv.CV_BGR2GRAY)
image2 = np.copy(image)
cProfile.runctx("refGetOrientationAndMagnitude(image)", globals=globals(), locals=locals())
cProfile.runctx("newGetOrientationAndMagnitude(image)", globals=globals(), locals=locals())
Which yielded the following results:
So 1.022s to 0.004, 255× faster with just replacing a double for
loop with an OpenCV call.