2-D Convolution is very useful to process image and to code various filters. A 2-D convolution can be thought of as replacing each pixel with the weighted sum of its neighbors. The kernel is another image, of smaller size, which contains the weights as illustrated below.
Depending on kernel and pixel values, weighted sum of pixel neighbors is signed (positive or negative) and can be greater than byte values [0..255] that are used to represents RGB values of the images.
In most of cases, 2D Convolution algorithms include an implicit cut-off which is similar to a binary filter: If the weighted sum is <= to 0, then weighted sum equals to 0 and if the weighted sum is > to 255, then weighted sum equals to 255.
In redCV, rcvConvolveMat function integrates this binary processing of weighted sum. However, depending on the nature of the image you process, this cut-off can induce more or less correct result when applying kernel filter.
Weighted sum normalization
A way to avoid this kind of problem is to use a normalization of the weighted sums. This is done by rcvConvolveNormalizedMat function in RedCV.
The idea is rather simple. First we look for the minimal (wsMin) and maximal (wsMax) weighted sums that are calculated when applying the kernel convolution. The difference
wsMax - wsMin
gives the range of the variation in image when convolution is applied. This allows to calculate a scale factor which is equal to 255 / (wsMax - wsMin)
. Then, it's really easy to remplace each weighted sum (wsValue) with this formula wsValue = (wsValue - wsMin) * scale
: each wsValue will be in 0..255 range.Code sample
Red [
Title: "Matrix tests "
Author: "Francois Jouen"
File: %matLaplacian.red
Needs: 'View
]
#include %../../libs/redcv.red ; for redCV functions
; laplacian convolution filter for sample
mask: [-1.0 0.0 -1.0 0.0 4.0 0.0 -1.0 0.0 -1.0]
isize: 512x512
bitSize: 32
img1: rcvCreateImage isize
img2: rcvCreateImage isize
img3: rcvCreateImage isize
loadImage: does [
canvas1/image/rgb: black
canvas2/image/rgb: black
canvas3/image/rgb: black
tmp: request-file
if not none? tmp [
img1: rcvLoadImage tmp
img2: rcvCreateImage img1/size
img3: rcvCreateImage img1/size
mat1: rcvCreateMat 'integer! bitSize img1/size
mat2: rcvCreateMat 'integer! bitSize img1/size
mat3: rcvCreateMat 'integer! bitSize img1/size
; Converts to grayscale image and to 1 Channel matrix [0..255]
rcvImage2Mat img1 mat1
; Standard Laplacian convolution
rcvConvolveMat mat1 mat2 img1/size mask 1.0 0.0
; Normalized Laplacian convolution
rcvConvolveNormalizedMat mat1 mat3 img1/size mask 1.0 0.0
; From matrices to Red images
rcvMat2Image mat2 img2
rcvMat2Image mat3 img3
; show results
canvas1/image: img1
canvas2/image: img2
canvas3/image: img3
rcvReleaseMat mat1
rcvReleaseMat mat2
rcvReleaseMat mat2
]
]
; ***************** Test Program ****************************
view win: layout [
title "Laplacian convolution on matrix"
button "Load" [loadImage]
button 60 "Quit" [ rcvReleaseImage img1
rcvReleaseImage img2
Quit]
return
text 100 "Source" pad 412x0
text 120 "Standard convolution"
pad 402x0
text "Normalized convolution"
return
canvas1: base isize img1
canvas2: base isize img2
canvas3: base isize img3
]