I want to apply a function similar to medianBlur to an image, but instead of using the 0.5 quantile as medianBlur is doing, I want to apply another quantile (e.g. 0.15).
Outside of my toy example, i'll use this with 1000x1000px images with a large kernel size.
I wrote an implementation (apply_quantileblur
) which is doing what I want, but non surprisingly it is quite slow.
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = np.array([[ 6, 249, 255, 255, 255, 249, 0, 6, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255],
[252, 3, 34, 255, 252, 255, 170, 255, 252, 135, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255],
[ 0, 0, 255, 255, 255, 255, 83, 255, 0, 255, 0, 255, 255,
255, 255, 255, 255, 255, 255, 255],
[255, 0, 0, 207, 255, 48, 255, 48, 255, 48, 255, 207, 255,
255, 207, 255, 255, 255, 255, 255],
[245, 245, 38, 0, 0, 255, 86, 9, 245, 255, 245, 9, 101,
255, 245, 255, 255, 255, 255, 255],
[ 0, 255, 32, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0,
255, 255, 255, 255, 255, 255, 255],
[255, 255, 32, 255, 255, 0, 83, 0, 255, 0, 0, 0, 255,
255, 255, 255, 255, 255, 255, 255],
[239, 239, 225, 0, 239, 255, 239, 0, 0, 141, 0, 0, 255,
255, 239, 15, 255, 255, 255, 255],
[ 0, 0, 201, 25, 255, 255, 229, 0, 25, 0, 229, 25, 25,
229, 229, 0, 255, 255, 255, 255],
[ 0, 0, 0, 0, 0, 0, 172, 0, 255, 0, 0, 255, 255,
0, 255, 255, 255, 255, 255, 255],
[120, 255, 117, 0, 120, 255, 134, 0, 0, 64, 255, 0, 134,
120, 120, 120, 255, 255, 255, 255],
[ 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 28,
0, 0, 76, 76, 76, 76, 255],
[ 0, 0, 223, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0,
0, 255, 255, 0, 255, 255, 255],
[255, 210, 45, 0, 45, 45, 45, 45, 45, 23, 0, 0, 0,
45, 45, 45, 210, 255, 255, 255],
[ 89, 0, 255, 255, 0, 255, 141, 165, 165, 42, 165, 165, 89,
0, 165, 89, 255, 255, 255, 255],
[255, 255, 11, 12, 12, 255, 255, 255, 0, 255, 12, 0, 242,
255, 255, 255, 255, 255, 255, 255],
[255, 255, 246, 9, 245, 245, 255, 9, 255, 5, 245, 255, 255,
255, 255, 255, 255, 255, 255, 255],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 0, 0,
255, 255, 255, 255, 255, 255, 255],
[255, 217, 228, 255, 255, 38, 255, 255, 217, 121, 255, 255, 231,
255, 255, 255, 255, 255, 255, 255],
[255, 255, 230, 255, 0, 226, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255]]).astype(np.uint8)
plt.imshow(img)
plt.title('original image')
plt.show()
median_blurred_img = cv2.medianBlur(img, 5)
plt.imshow(median_blurred_img)
plt.title('median blurred image')
plt.show()
def apply_quantileblur(img, kernel_size, quantile):
rows, cols = img.shape
newmat = np.zeros((rows, cols))
kerny = int((kernel_size - 1) / 2)
for r in range(rows):
rmin = max(r - kerny, 0)
rmax = min(r + kerny + 1, rows)
for c in range(cols):
cmin = max(c - kerny, 0)
cmax = min(c + kerny + 1, cols)
miniimg = img[rmin:rmax, cmin:cmax]
newmat[r, c] = np.quantile(miniimg, quantile)
return newmat
quantile_blurred_img = apply_quantileblur(img, 5, 0.15)
plt.imshow(quantile_blurred_img)
plt.title('0.15 quantile blurred image')
plt.show()
quantile_blurred_img = apply_quantileblur(img, 5, 0.85)
plt.imshow(quantile_blurred_img)
plt.title('0.85 quantile blurred image')
plt.show()
Which outputs:
Since medianBlur is doing a very similar thing about 1000x quicker, I was wondering how to improve this process to actually be usable.
EDIT:
I've run a comparison between the 4 different methods there are so far:
from scipy.ndimage import generic_filter
import time
import diplib
# sudo apt install default-jre
# sudo apt-get install freeglut3
import cv2
import numpy as np
from matplotlib import pyplot as plt
def show_img(img, title):
plt.imshow(img)
plt.title(title)
plt.show()
def apply_quantileblur_manual(img, kernel_size, quantile):
rows, cols = img.shape
newmat = np.zeros((rows, cols))
kerny = int((kernel_size - 1) / 2)
for r in range(rows):
rmin = max(r - kerny, 0)
rmax = min(r + kerny + 1, rows)
for c in range(cols):
cmin = max(c - kerny, 0)
cmax = min(c + kerny + 1, cols)
miniimg = img[rmin:rmax, cmin:cmax]
newmat[r, c] = np.quantile(miniimg, quantile)
return newmat
def apply_quantileblur_generic(img, kernel_size, quantile):
return generic_filter(img, lambda x: np.quantile(x, quantile), size=kernel_size)
def medianblur(img, kernel_size):
return cv2.medianBlur(img, kernel_size)
def diplib_blur(img, kernel_size, quantile):
k = diplib.Kernel([kernel_size,kernel_size])
z = diplib.PercentileFilter(img, quantile, k)
return z
img = np.random.randint(0,255,(500, 500), np.uint8)
cv2.imwrite('ugh.tiff', img)
dip_img = diplib.ImageReadTIFF('ugh.tiff')
kernel_size = 111
quantile = 50
start = time.time()
manual_img = apply_quantileblur_manual(img, kernel_size, quantile / 100)
print(time.time() - start)
start = time.time()
generic_img = apply_quantileblur_generic(img, kernel_size, quantile / 100)
print(time.time() - start)
start = time.time()
median_img = medianblur(img, kernel_size)
print(time.time() - start)
start = time.time()
diplib_img = diplib_blur(img, kernel_size, quantile)
print(time.time() - start)
which prints:
31.994836568832397
46.67619204521179
0.005266904830932617
1.999727725982666
So OpenCV is actually 10.000 faster, but without the option to specify the quantile.
from Using medianBlur with a different quantile
No comments:
Post a Comment