Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a mask with an adaptive threshold?

Tags:

c++

opencv

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

like image 804
Quentin Geissmann Avatar asked Mar 23 '12 15:03

Quentin Geissmann


People also ask

What is adaptive thresholding technique?

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.

What is the need of adaptive thresholding?

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.

What is the difference between global and adaptive thresholding?

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.

What is block size in adaptive threshold?

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.


1 Answers

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:

  1. Sets masked pixels in the image to zero.
  2. Determines the number of unmasked neighbours within the convolution block for each pixel.
  3. Performs a convolution, and averages it by the number of unmasked neighbours within the block. This yields the average value within a pixels neighbourhood block.
  4. Thresholds, by comparing the image to the mean neighbourhood values, mean_conv
  5. Adds the masked off (non-thresholded) part of the image back on.

enter image description here

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.

like image 137
fraxel Avatar answered Nov 03 '22 19:11

fraxel