I am trying to do a Sepia-Filter using OpenCV & filter2D with a kernel, however from what I see is that the results that I get this way are differing from a manual implementation with a nested for-loop. My understanding of the cv.filter2D seems to be "off" and I hope someone can enlighten me where my thinking goes haywire...
The image is a small 10 x 10 pixel image (simple to check on the content) with some blue and white. The approach for OpenCV is to create a kernel with the appropriate weights for the pixel, however I guess I understood that wrongly. The kernel has this format:
kernel = np.array([[0.272, 0.534, 0.131],
[0.349, 0.686, 0.168],
[0.393, 0.769, 0.189]])
return cv2.filter2D(image, -1, kernel)
Running an image through that results in some bright yellow instead of the expected brown-tone. Doing the manual approach using the mentioned nested for-loops I do it this way:
dimensions = image.shape
newR=np.zeros([dimensions[0],dimensions[1]])
newG=np.zeros([dimensions[0],dimensions[1]])
newB=np.zeros([dimensions[0],dimensions[1]])
for x in range(0, dimensions[0]):
for y in range(0, dimensions[1]):
newR[x,y] = int(0.272*r[x,y]+0.534*g[x,y]+0.131*b[x,y])
newG[x,y] = int(0.349*r[x,y]+0.686*g[x,y]+0.168*b[x,y])
newB[x,y] = int(0.393*r[x,y]+0.769*g[x,y]+0.189*b[x,y])
if newR[x,y]>255:
newR[x,y]=255
if newG[x,y]>255:
newG[x,y]=255
if newB[x,y]>255:
newB[x,y]=255
output_man = cv2.merge ( (newR, newG, newB) )
This then creates an image of the expected brownish colors.
I was expecting the filter2D to do a convolution of the pixels of the original image the same way as my manual code but it seems I am missing something and would hope for some pointers.
Originally I was reading in a *.png but for a runnable example I am creating the image in a separate function. This here is a full python-script that shows the two different results:
import cv2
import numpy as np
import scipy
def create_image():
r_arr= np.array(
[[255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
[255, 255, 255, 255, 255, 255, 255, 255, 255, 0],
[255, 255, 255, 255, 255, 255, 255, 0, 0, 0],
[255, 255, 255, 255, 255, 0, 0, 0, 0, 0],
[255, 255, 255, 255, 0, 0, 0, 0, 0, 0],
[255, 255, 0, 0, 0, 0, 0, 0, 0, 0],
[255, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 255, 255],
[ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255],
[ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255]]
, dtype = np.uint8)
g_arr= np.array(
[[255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
[255, 255, 255, 255, 255, 255, 255, 255, 255, 162],
[255, 255, 255, 255, 255, 255, 255, 162, 162, 162],
[255, 255, 255, 255, 255, 162, 162, 162, 162, 162],
[255, 255, 255, 255, 162, 162, 162, 162, 162, 162],
[255, 255, 162, 162, 162, 162, 162, 162, 162, 162],
[255, 162, 162, 162, 162, 162, 162, 162, 162, 162],
[162, 162, 162, 162, 162, 162, 162, 162, 255, 255],
[162, 162, 162, 162, 162, 162, 162, 255, 255, 255],
[162, 162, 162, 162, 162, 255, 255, 255, 255, 255]]
,dtype = np.uint8)
b_arr= np.array(
[[255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
[255, 255, 255, 255, 255, 255, 255, 255, 255, 232],
[255, 255, 255, 255, 255, 255, 255, 232, 232, 232],
[255, 255, 255, 255, 255, 232, 232, 232, 232, 232],
[255, 255, 255, 255, 232, 232, 232, 232, 232, 232],
[255, 255, 232, 232, 232, 232, 232, 232, 232, 232],
[255, 232, 232, 232, 232, 232, 232, 232, 232, 232],
[232, 232, 232, 232, 232, 232, 232, 232, 255, 255],
[232, 232, 232, 232, 232, 232, 232, 255, 255, 255],
[232, 232, 232, 232, 232, 255, 255, 255, 255, 255]]
,dtype = np.uint8)
art_image=cv2.merge((r_arr, g_arr, b_arr))
return art_image
def sepia(image):
#Calculations for every pixel:
# new R-value = tr = int(0.393 * r + 0.769 * g + 0.189 * b) but maxed at 255
# new G-value = tg = int(0.349 * r + 0.686 * g + 0.168 * b) but maxed at 255
# new B-value = tb = int(0.272 * r + 0.534 * g + 0.131 * b) but maxed at 255
#This block is original for RGB
kernel = np.array([[0.272, 0.534, 0.131],
[0.349, 0.686, 0.168],
[0.393, 0.769, 0.189]])
return cv2.filter2D(image, -1, kernel)
######################################################################
######################################################################
######################################################################
#image = cv2.imread("10x10.png")
image = create_image()
dimensions = image.shape
b, g, r = cv2.split(image)
output_image=sepia(image)
dimensions = image.shape
newR=np.zeros([dimensions[0],dimensions[1]])
newG=np.zeros([dimensions[0],dimensions[1]])
newB=np.zeros([dimensions[0],dimensions[1]])
for x in range(0, dimensions[0]):
for y in range(0, dimensions[1]):
newR[x,y] = int(0.272*r[x,y]+0.534*g[x,y]+0.131*b[x,y])
newG[x,y] = int(0.349*r[x,y]+0.686*g[x,y]+0.168*b[x,y])
newB[x,y] = int(0.393*r[x,y]+0.769*g[x,y]+0.189*b[x,y])
if newR[x,y]>255:
newR[x,y]=255
if newG[x,y]>255:
newG[x,y]=255
if newB[x,y]>255:
newB[x,y]=255
output_man = cv2.merge ( (newR, newG, newB) )
b2, g2, r2 = cv2.split(output_image)
print(r)
print("--------")
print(g)
print("--------")
print(b)
print("--------")
print("--------")
print(r2)
print("--------")
print(g2)
print("--------")
print(b2)
print("--------")
print("--------")
print(newR)
print("--------")
print(newG)
print("--------")
print(newB)
print("--------")
cv2.imwrite("10x10_m.png", output_man)
cv2.imwrite("10x10_f.png", output_image)