Ask Your Question
5

Smoothing with a mask

asked 2012-10-09 17:57:40 -0500

HugoRune gravatar image

updated 2012-10-09 18:13:38 -0500

Is there a way to apply a blur or median smoothing filter to an image, while supplying a mask of pixels that should be ignored?

I have a height map from a laser-scanner which I want to smooth. The map is not continuous; wherever the laser was not reflected, the map simply contains no height data.

If I arbitrarily set the height for missing values to zero (or any other value) and then blur the image, this will introduce a lot of error around these missing values, and around all edges and holes of objects.

So I need to supply a binary mask to the blur. Masked pixels should be ignored when calculating the new blur or median value of neighbor pixels.

How can I accomplish this?

edit retag flag offensive close merge delete

3 answers

Sort by ยป oldest newest most voted
4

answered 2013-03-06 03:39:19 -0500

Pieter-Jan Busschaert gravatar image

updated 2013-03-06 03:40:31 -0500

assuming mask where 255 = valid pixel, 0 = invalid pixel

pseudo-code written for Python - OpenCV

image[mask == 0] = 0
blurred_image = cv2.blur(image)
blurred_mask = cv2.blur(mask)
result = blurred_image / blurred_mask
edit flag offensive delete link more

Comments

Great answer!
Although you write "assuming mask where 255 = valid pixel..." the code actually seems to assume that the mask is binary with 0/1. To use 255, the last line should be result = 255 * blurred_image / blurred_mask.

Adi gravatar imageAdi ( 2014-07-10 05:09:01 -0500 )edit

Is there any mathematical explanation for this approach to prove that this is correct?

hahne gravatar imagehahne ( 2016-02-01 08:51:11 -0500 )edit

I previously claimed this didn't work. However, I missed the image[mask == 0] = 0 part at the beginning. I agree that this is mathematically correct.

jbrownkramer gravatar imagejbrownkramer ( 2018-08-23 09:21:36 -0500 )edit

Is there any mathematical explanation for this approach to prove that this is correct?

According to the documentation, for smoothing, you add up all the intensities in the neighborhood of a pixel and divide it by the size of the neighborhood, i.e. you take the average. Pixels that are black contribute 0 to the average. Thus, to adjust the average to only include non-black pixels, you need to adjust the size you used for smoothing.

This adjusted size (actually the scale) is obtained by blurred_mask = cv2.blur(mask). E.g. if you have a 3-by-3 kernel for smoothing and 4 pixels are black, the value at the kernel's center will be 5/9. Dividing by it multiplies you result by 9/5, i.e. you only divided by 5 eventually.

mfischer-gundlach gravatar imagemfischer-gundlach ( 2019-08-12 04:28:51 -0500 )edit

image_blurred = image.copy() image_blurred[mask == 0] = 0 image_blurred = cv2.blur(image_blurred, kernel_size) mask_blurred = cv2.blur(mask, kernel_size)

image_blurred[mask != 0] = image_blurred[mask != 0] / mask_blurred[mask != 0] image_blurred[mask == 0] = image[mask == 0]

mfischer-gundlach gravatar imagemfischer-gundlach ( 2019-08-12 07:32:57 -0500 )edit

`image_blurred = image.copy() image_blurred[mask == 0] = 0 image_blurred = cv2.blur(image_blurred, kernel_size) mask_blurred = cv2.blur(mask, kernel_size)

image_blurred[mask != 0] = image_blurred[mask != 0] / mask_blurred[mask != 0] image_blurred[mask == 0] = image[mask == 0]`

mfischer-gundlach gravatar imagemfischer-gundlach ( 2019-08-12 07:33:37 -0500 )edit

image_blurred = image.copy() image_blurred[mask == 0] = 0 image_blurred = cv2.blur(image_blurred, kernel_size) mask_blurred = cv2.blur(mask, kernel_size)

image_blurred[mask != 0] = image_blurred[mask != 0] / mask_blurred[mask != 0]
image_blurred[mask == 0] = image[mask == 0]
mfischer-gundlach gravatar imagemfischer-gundlach ( 2019-08-12 07:34:17 -0500 )edit
2

answered 2012-10-14 03:03:36 -0500

Michael Burdinov gravatar image

updated 2012-10-14 03:35:01 -0500

You will have to combine number of functions to do this, because existing blur functions don't use mask. Here an example (I assume that value of maskImage is 255 if pixel is relevant, and 0 if pixel is irrelevant):

erode(maskImage,maskImage,Mat()); // values near irrelevant pixels should not be changed
blur(sourceImage,bluredImage,Size(3,3));
bluredImage = sourceImage + ((bluredImage-sourceImage) & maskImage);

Last operation is combination of two images sourceImage and bluredImage. Values where maskImage was 255 will come from bluredImage, and values where maskImage was 0 will come from sourceImage.

edit flag offensive delete link more

Comments

Your proposed method is incorrect.This technique is frequently used in the field of fingerprint.For each kernel ,which is one on the border ,You have to clear values of current kernel do not belong to mask image(sum of the coefficient must be one).

Mostafa Sataki gravatar imageMostafa Sataki ( 2013-01-19 13:11:04 -0500 )edit

You can try it yourself to see that it works properly. After apply of erode() all pixels on border will get value 0 in maskImage. And sum of coefficients is one, so no problem here either. AIso am not sure I understand your remark about how it is used for fingerprints.

Michael Burdinov gravatar imageMichael Burdinov ( 2013-01-20 03:17:00 -0500 )edit

In the fingerprint field ,for noise reduction in the orientation map we have to smooth it by mask.This mask is generate in the quality process that there are holes in it.

Mostafa Sataki gravatar imageMostafa Sataki ( 2013-01-20 09:53:21 -0500 )edit

How can you do the & operation, when the number of channels is not same.

arqam gravatar imagearqam ( 2017-07-20 21:34:55 -0500 )edit
0

answered 2012-10-13 08:43:24 -0500

Rui Marques gravatar image

I think this answer from Alexander Smorkalov at OpenCV4Android group, suits you:

https://groups.google.com/d/topic/android-opencv/qSvJqaDdC_4/discussion

Quoting Alexander Smorkalov, for reference:

You can use several different ways to create mask for image. If you create images your self you can use alpha-channel as a mask. You need to save image in *.png format, and then load it with flags =-1 in imread. Then split it with cv::split function and path alpha channel as a mask.

If you cannot use alpha channel you can create mask using threshold ( http://docs.opencv.org/trunk/modules/imgproc/doc/miscellaneous_transformations.html?highlight=threshold#cv.Threshold )

edit flag offensive delete link more

Comments

1

There is a misunderstanding here: creating the mask is no problem, I already have the mask. But how can I smooth wile using that mask? the cvSmooth function does not take a mask parameter. And simply blurring first and then erasing the masked values does not work, the values of masked pixels will "seep" to neighboring pixels.

HugoRune gravatar imageHugoRune ( 2012-10-13 14:40:07 -0500 )edit
Login/Signup to Answer

Question Tools

1 follower

Stats

Asked: 2012-10-09 17:57:40 -0500

Seen: 15,401 times

Last updated: Mar 06 '13