Opencv Python Pixel-by-pixel Loop Is Slow
Solution 1:
I would suggest making this vectorized where you first find the circles that have a hue between 26 and 35 as well as hues between 36 and 77, then draw them on your image. cv2.circle
is unfortunately not vectorized as it is designed to only take a single pair of coordinates but the search for the pixels of interest can be done faster.
Try:
import numpy as np
import cv2
img = cv2.imread("green bottle.jpg")
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hue = imgHSV[...,0]
(rows1, cols1) = np.where(np.logical_and(hue >= 26, hue <= 35))
(rows2, cols2) = np.where(np.logical_and(hue >= 36, hue <= 77))
for r, c inzip(rows1, cols1):
cv2.circle(img, (c, r), 1, (255, 0, 0))
for r, c inzip(rows2, cols2):
cv2.circle(img, (c, r), 1, (0, 255, 0))
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
numpy.where
takes in a NumPy array and a condition to check for such that any values that meet this condition, we can obtain the row and column locations. I also use numpy.logical_and
to properly incorporate the range search for the hues. I do this twice, once per range of hues which gives me two sets of coordinates. I then loop over each set of coordinates and plot the circles in their respective colours.
Solution 2:
I had a try and came up with something pretty much identical to @rayryeng like this but it was very slow for my image:
# Load image
im = cv2.imread('smooth.png')
# Convert to HSV and extract H
H = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)[..., 0]
# Mask Hues in range [26..35]
rangeA = np.logical_and(H>=26, H<=35)
# Get coordinates of selected pixels and plot circle at each
for y,x in np.argwhere(rangeA):
cv2.circle(im, (x,y), 1, (255,255,255))
When I timed it, I realised all the time was taken by cv2.circle()
. So I looked at the circle of radius 1 and it looks like this:
0 1 0
1 0 1
0 1 0
which is extremely similar to a 3x3 morphological cross:
0 1 0
1 1 1
0 1 0
So, I drew the circles with morphology rather than cv2.circle()
, and I got this:
#!/usr/bin/env python3
import cv2
import numpy as np
# Load image
im = cv2.imread('smooth.png')
# Convert to HSV and extract H
H = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)[..., 0]
# Mask Hues in range [26..35]
rangeA = np.logical_and(H>=26, H<=35)
# Note that a circle radius 1 is a 3x3 cross, so rather than# draw circles we can convolve with a ring
ring = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
ring[1,1]=0
res = cv2.morphologyEx((rangeA*255).astype(np.uint8), cv2.MORPH_DILATE, ring)
# Save result
cv2.imwrite('result.png', res)
So, if I start with this:
I got this in a fraction of the time:
Timings on my image were 5ms as follows with for
loop:
In [133]: %%timeit
...: for y,x in np.argwhere(rangeA):
...: cv2.circle(im, (x,y), 1, (255,255,255))
...:
4.98 ms ± 42.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
and 159 microseconds with dilation:
%timeit cv2.morphologyEx((rangeA*255).astype(np.uint8), cv2.MORPH_DILATE, ring)
159 µs ± 4.13 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Post a Comment for "Opencv Python Pixel-by-pixel Loop Is Slow"