I am writing a small program in C++ using OpenCV-2.3 API. I have an issue processing an adaptive threshold using a non rectangular mask.
So far, I was performing the adaptive threshold on the whole image and masking afterwards. I realise that,in my case , this was a mistake since the masked pixels would be used to calculate the threshold of my pixels of interest (while I simply want to exclude the former from the analysis)... However, unlike functions such as cv:: norm, cv::adaptiveThreshold does not seem to support explicitly a mask.
Do you know any obvious solution or workaround? Thank you very muck for your suggestions, Quentin
Adaptive thresholding works by using machine learning techniques to analyze historical data, sometimes displayed in a histogram, for patterns that help define the normal state of your environment.
Like global thresholding, adaptive thresholding is used to separate desirable foreground image objects from the background based on the difference in pixel intensities of each region.
Global thresholding determines the threshold value based on the histogram of the overall pixel intensity distribution of the image. In contrast, adaptive thresholding computes the threshold value for each fractional region of the image, so that each fractional region has a different threshold value.
blockSize- Size of a pixel neighborhood that is used to calculate a threshold value for the pixel. C- Constant subtracted from the mean or weighted mean (see the details below). Normally, it is positive but maybe zero or negative as well.
I've written some Python (sorry not c++) code that will allow for masked adaptive thresholding. Its not very fast, but it does what you want, and you may be able to use it as a basis for C++ code. It works as follows:
mean_conv
The images show, the initial image, the mask, the final processed image.
Here's the code:
import cv
import numpy
from scipy import signal
def thresh(a, b, max_value, C):
return max_value if a > b - C else 0
def mask(a,b):
return a if b > 100 else 0
def unmask(a,b,c):
return b if c > 100 else a
v_unmask = numpy.vectorize(unmask)
v_mask = numpy.vectorize(mask)
v_thresh = numpy.vectorize(thresh)
def block_size(size):
block = numpy.ones((size, size), dtype='d')
block[(size - 1 ) / 2, (size - 1 ) / 2] = 0
return block
def get_number_neighbours(mask,block):
'''returns number of unmasked neighbours of every element within block'''
mask = mask / 255.0
return signal.convolve2d(mask, block, mode='same', boundary='symm')
def masked_adaptive_threshold(image,mask,max_value,size,C):
'''thresholds only using the unmasked elements'''
block = block_size(size)
conv = signal.convolve2d(image, block, mode='same', boundary='symm')
mean_conv = conv / get_number_neighbours(mask,block)
return v_thresh(image, mean_conv, max_value,C)
image = cv.LoadImageM("image.png", cv.CV_LOAD_IMAGE_GRAYSCALE)
mask = cv.LoadImageM("mask.png", cv.CV_LOAD_IMAGE_GRAYSCALE)
#change the images to numpy arrays
original_image = numpy.asarray(image)
mask = numpy.asarray(mask)
# Masks the image, by removing all masked pixels.
# Elements for mask > 100, will be processed
image = v_mask(original_image, mask)
# convolution parameters, size and C are crucial. See discussion in link below.
image = masked_adaptive_threshold(image,mask,max_value=255,size=7,C=5)
# puts the original masked off region of the image back
image = v_unmask(original_image, image, mask)
#change to suitable type for opencv
image = image.astype(numpy.uint8)
#convert back to cvmat
image = cv.fromarray(image)
cv.ShowImage('image', image)
#cv.SaveImage('final.png',image)
cv.WaitKey(0)
After writing this I found this great link that has a good explanation with plenty of image examples, I used their text image for the above example.
Note. Numpy masks do not seem to be respected by scipy signal.convolve2d()
, so the above workarounds were necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With