我想实现一个本地阈值算法,我需要你的专业知识。
我的图片大小调整为600x400,灰度。
本地化的基本思维过程:
创建一个9x9内核。
条件:
我的问题是:
我应该如何选择我的内核/面具?
cv::Mat ROI;
cv::Mat mask(input.size(),CV_8UC1, cv::Scalar::all(0)); // create mask of 0s at first
const int kerneldepth = 1;
const int kernelsize = 9;
cv::Mat kernel = cv::Mat::ones( kernelsize, kernelsize, CV_8UC1 );
//take ROI of 9x9 and apply a threshold
for( double x = 9; x < input.cols -9; x++ ){
for( double y = 9 ; y < input.rows - 9 ; y++ ){
try{
double x_left = x - 4;
double x_right = x + 4;
double y_up = y + 4;
double y_down = y - 4;
double maxVal;
double minVal;
cv::Point anchor(kernelsize/2,kernelsize/2);
cv::Rect ROI = cv::Rect(x_left,y_down,9,9);
cv::Mat ROI_Mat = input(ROI); // a new matrix for ROI
cv::Scalar avgPixelIntensity = cv::mean( ROI_Mat ); // calculate mean
cv::minMaxLoc(ROI_Mat,&minVal,&maxVal);
if( input.at<uchar>(x,y) >= 0.5*maxVal){
cv::filter2D(input,mask,-1,kernel,anchor,0);
} else { break;}
}
catch (cv::Exception &e){
e.what();
}
}
*****************************UPDATED CODE: ******************************************
applyLocalThresh(cv::Mat &src, cv::Mat& out){
double maxVal, minVal;
cv::Mat output;
int top, bottom, left , right;
int borderType = cv::BORDER_CONSTANT;
cv::Scalar value;
top = (int) (9); bottom = (int) (9);
left = (int) (9); right = (int) (9);
output = src;
out = src;
value = 0;
cv::copyMakeBorder(src,output,top,bottom,left,right,borderType,value);
for(int y = 9; y < src.rows; y++) {
for(int x = 9; x < src.cols; x ++) {
cv::Mat ROI = src(cv::Rect(cv::Point(x-4,y-4),cv::Size(9,9)));
cv::minMaxLoc(ROI,&minVal,&maxVal);
if(src.at<uchar>(cv::Point(x-4,y-4)) >= 0.6*maxVal){
out.at<uchar>(cv::Point(x-4,y-4)) = 255;
}else{
out.at<uchar>(cv::Point(x-4,y-4));
}
}
}
}
答案 0 :(得分:3)
你可以通过扩张然后在OpenCV中进行比较来做到这一点;
im = load image here;
di = dilate im with a 9x9 kernel;
bw = im > (di * 0.5); // in OpenCV, pixels of bw are set to 255 or 0
一个简单的例子用Matlab / Octave中的4x6图像和3x3内核来说明这一点:
im =
1 2 3 4 5 6
2 3 4 5 6 7
3 4 5 6 7 8
4 5 6 7 8 9
di =
3 4 5 6 7 7
4 5 6 7 8 8
5 6 7 8 9 9
5 6 7 8 9 9
th = di * .5
th =
1.5000 2.0000 2.5000 3.0000 3.5000 3.5000
2.0000 2.5000 3.0000 3.5000 4.0000 4.0000
2.5000 3.0000 3.5000 4.0000 4.5000 4.5000
2.5000 3.0000 3.5000 4.0000 4.5000 4.5000
bw = im&gt;个
bw =
0 0 1 1 1 1
0 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
答案 1 :(得分:2)
我担心这种做法并不完全正确。让我解释一下:对于涉及内核的操作,必须小心将内核的中心放在要转换的像素之上。这是因为3x3,5x5,7x7,9x9(...)内核只计算图像中一个像素的值,这是位于中心的那个[0,0]内核。
如果考虑如何计算图像第一个像素的值,9x9内核的中心将放置在坐标 [0,0] 处。这意味着3/4的内核将被放置在负坐标处,即坐标指的是不存在的像素:
[-4,-4][-3,-4][-2,-4][-1,-4][ 0,-4][ 1,-4][ 2,-4][ 3,-4][ 4,-4]
[-4,-3][-3,-3][-2,-3][-1,-3][ 0,-3][ 1,-3][ 2,-3][ 3,-3][ 4,-3]
[-4,-2][-3,-2][-2,-2][-1,-2][ 0,-2][ 1,-2][ 2,-2][ 3,-2][ 4,-2]
[-4,-1][-3,-1][-2,-1][-1,-1][ 0,-1][ 1,-1][ 2,-1][ 3,-1][ 4,-1]
[-4, 0][-3, 0][-2, 0][-1, 0][ 0, 0][ 1, 0][ 2, 0][ 3, 0][ 4, 0]
[-4, 1][-3, 1][-2, 1][-1, 1][ 0, 1][ 1, 1][ 2, 1][ 3, 1][ 4, 1]
[-4, 2][-3, 2][-2, 2][-1, 2][ 0, 2][ 1, 2][ 2, 2][ 3, 2][ 4, 2]
[-4, 3][-3, 3][-2, 3][-1, 3][ 0, 3][ 1, 3][ 2, 3][ 3, 3][ 4, 3]
[-4, 4][-3, 4][-2, 4][-1, 4][ 0, 4][ 1, 4][ 2, 4][ 3, 4][ 4, 4]
对于图像边界附近的像素,总会发生这种情况。因此,对于第一个像素的计算,我们必须将计算限制在内核的1/4,指目标图像中的有效坐标:
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ 0, 0][ 1, 0][ 2, 0][ 3, 0][ 4, 0]
[ ][ ][ ][ ][ 0, 1][ 1, 1][ 2, 1][ 3, 1][ 4, 1]
[ ][ ][ ][ ][ 0, 2][ 1, 2][ 2, 2][ 3, 2][ 4, 2]
[ ][ ][ ][ ][ 0, 3][ 1, 3][ 2, 3][ 3, 3][ 4, 3]
[ ][ ][ ][ ][ 0, 4][ 1, 4][ 2, 4][ 3, 4][ 4, 4]
因此,您当前的方法存在的问题是,在某些时候您将设置一个具有负坐标的ROI,并且当执行这些指令时,您将看到一个不错的崩溃:
cv::Mat ROI_Mat = input(ROI); // crash
解决方案不是使用ROI而是自己实现该算法。我无法看到此自定义计算与cv::filter2D()
一起使用。这里有一些东西可以帮助您入门:
void local_threshold(const cv::Mat& input, cv::Mat& output)
{
if (input.channels() != 1)
{
std::cout << "local_threshold !!! input image must be single channel" << std::endl;
return;
}
output = cv::Mat(input.rows, input.cols, CV_8UC1);
double min_val = 0, max_val = 0;
for (int i = 0; i < input.rows; i++)
for (int j = 0; j < input.cols; j++)
{
cv::Mat kernel = Mat::zeros(9, 9, output.type());
// Implement logic to fill the 9x9 kernel with
// values from the input Mat, respecting boundaries.
cv::Scalar avg_intensity = cv::mean(kernel);
cv::minMaxLoc(kernel, &min_val,&max_val);
if (input.at<uchar>(i,j) > (max_val / 2))
output.at<unsigned char>(i,j) = 255;
else
output.at<unsigned char>(i,j) = 0;
}
}
答案 2 :(得分:2)
在进一步思考并找到如何利用我在编程方面的基本知识后,我想出了这个代码并不是最有效但完成工作的代码。
我的方法的主要问题是什么? :
我解决这个问题的方法是什么?:
结果:
代码:
double maxVal, minVal;
cv::Mat output;
int top, bottom, left , right;
int borderType = cv::BORDER_CONSTANT;
cv::Scalar value;
top = (int) (4); bottom = (int) (4);
left = (int) (4); right = (int) (4);
output = src;
out = src;
value = 0;
cv::copyMakeBorder(src,output,top,bottom,left,right,borderType,value);
for(int y = 4; y < output.rows - 4; y++) {
for(int x = 4; x < output.cols - 4; x ++) {
// apply local ROI
cv::Mat ROI = output(cv::Rect(cv::Point(x-4,y-4),cv::Size(9,9)));
cv::minMaxLoc(ROI,&minVal,&maxVal); // extract max intensity values in the ROI
if(src.at<uchar>(cv::Point(x-4,y-4)) >= 0.5*maxVal){ // apply local threshold w.r.t highest intensity level
out.at<uchar>(cv::Point(x-4,y-4)) = 255; // change pixel value in mask if true
}else{
out.at<uchar>(cv::Point(x-4,y-4)) = 0;
}
}
}
}
它需要一些清理我知道,但希望这会帮助其他人得到一些想法。