2012-03-23 2 views
7

Я пишу небольшую программу на C++, используя API OpenCV-2.3. У меня проблема с адаптивным порогом с использованием не прямоугольной маски.Использование маски с адаптивным порогом?

До сих пор я выполнял адаптивный порог для всего изображения и затем маскировал его. Я понимаю, что в моем случае это было ошибкой, поскольку маскированные пиксели использовались для вычисления порога моих пикселей, представляющих интерес (хотя я просто хочу исключить первое из анализа) ... Однако, в отличие от таких функций, как как cv :: norm, cv :: adaptiveThreshold явно не поддерживает маску.

Вы знаете какое-либо очевидное решение или обходное решение? Большое спасибо за ваши предложения, Quentin

ответ

3

Я написал код Python (извините, не C++), который позволит маскировать адаптивное пороговое значение. Это не очень быстро, но он делает то, что вы хотите, и вы можете использовать его в качестве основы для кода на C++. Он работает следующим образом:

  1. Устанавливает замаскированные пиксели в изображении на ноль.
  2. Определяет количество незамаскированных соседей в блоке свертки для каждого пикселя.
  3. Выполняет свертку и усредняет ее количеством невидимых соседей внутри блока. Это дает среднее значение в блоке окрестности пикселей.
  4. Пороговые значения, путем сравнения изображений со значениями средней окрестности, mean_conv
  5. Добавляет скрытую (не пороговую) часть изображения обратно.

enter image description here

Изображения показывают, исходное изображение, маску, окончательный обработанное изображение.

Вот код:

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) 

После написания этого я нашел this great link, что есть хорошее объяснение с большим количеством примеров изображений, я использовал их изображение текста для приведенного выше примера.

Примечание. Ненужные маски, похоже, не соблюдаются scipy signal.convolve2d(), поэтому описанные выше обходные пути были необходимы.

+0

Большое спасибо за ваш ответ. Я исследую ваше предложение. Quentin –

+0

@Quentin Geissmann - Вы получили его в своем коде на C++? – fraxel

+0

Я думаю, что я понял, как реализовать, не делая его очень медленным ... Но еще не реализовано. :) спасибо –

3

По вашим советам, и после прочтения вашей ссылки я написал эту небольшую C++-функцию: Это всего лишь 1,5 медленнее, чем адаптивный порог, но я, вероятно, смогу его улучшить.

void adaptiveThresholdMask(const cv::Mat src,cv::Mat &dst, double maxValue,  cv::Mat mask, int thresholdType, int blockSize, double C){ 
cv::Mat img, invertMask, noN, conv,kernel(cv::Size(blockSize,blockSize),CV_32F); 

/* Makes a image copy of the source image*/ 
src.copyTo(img); 

/* Negates the mask*/ 
cv::bitwise_not(mask,invertMask); 

/* Sets to 0 all pixels out of the mask*/ 
img = img-invertMask; 
/* The two following tasks are both intensive and 
* can be done in parallel (here with OpenMP)*/ 
#pragma omp parallel sections 
{ 
    { 
     /* Convolves "img" each pixels takes the average value of all the pixels in blocksize*/ 
     cv::blur(img,conv,cv::Size(blockSize,blockSize)); 
    } 
    #pragma omp section 
    { 
     /* The result of bluring "mask" is proportional to the number of neighbours */ 
     cv::blur(mask,noN,cv::Size(blockSize,blockSize)); 
    } 
} 

/* Makes a ratio between the convolved image and the number of 
* neighbours and subtracts from the original image*/ 
if(thresholdType==cv::THRESH_BINARY_INV){ 
    img=255*(conv/noN)-img; 
    } 
else{ 
    img=img-255*(conv/noN); 
    } 

/* Thresholds by the user defined C*/ 
cv::threshold(img,dst,C,maxValue,cv::THRESH_BINARY); 

/* We do not want to keep pixels outside of the mask*/ 
cv::bitwise_and(mask,dst,dst); 

} 

Еще раз спасибо

+0

nice one :) Я думаю, что мог бы значительно ускорить мой код на Python, я попытаюсь в какой-то момент приблизиться к нему. – fraxel

+0

Я не понимаю этого решения. Можете ли вы рассказать, как работает эта версия ускорения? Я попытался перенести его на opencv-python, но без успеха – pzo

+0

Это очень приятное решение и очень быстрое (проверено на Python), хотя оно становится немного неточным, когда ваше изображение очень темное, потому что тогда вы царапаете целочисленное разрешение из-за операции размытия. – letmaik

Смежные вопросы