二值化是图像处理的基本操作,OpenCV提供了:
固定阈值二值化.
--threshold(... ...,CV_THRESH_BINARY);
最大类间方差法(大津法OTSU)二值化.
--threshold(... ...,CV_THRESH_OTSU);
局部自适应二值化.
--adaptiveThreshold(... ...);
除以上方法外,P参数法(按照预定的模式亮度占比二值化),也是实用的二值化方法,OpenCV没有提供,可以自己封装.
#include <opencv2/opencv.hpp> #include <iostream> using namespace std; using namespace cv; /* C_FUNC:pValueThreshold 功能 :p参数法,将Mat图像二值化,并输出. 参数: Frame:输入图像 maxValue:预估目标范围的最大值比例,取值[0~255] ratio:p参数占比 valueTop:二值化后亮度区的取值 返回:成功返回0,不满足条件返回-1 附注: maxValue不是亮度取值,而是亮度比例,要将亮度映射到[0~255]区间. 输入的图像会被二值化,如果要保存原图,请复制后再处理. */ int pValueThreshold(Mat Frame, int maxValue, double ratio, int valueTop) { //直方图均衡化 equalizeHist(Frame, Frame); //计算直方图 Mat hist; //将要获得的直方图 int imgNum = 1; //图像数量 int histDim = 1; //直方图维度 int histSize = 256; //每个维度的bin个数 float range[] = { 0,256 }; //每个维度的统计范围 const float* histRange = { range }; bool accumulate = false; calcHist(&Frame, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false); //hist中的第一行,是直方图的值. float* p = hist.ptr<float>(0); //循环计算阈值 int countPix = Frame.cols * Frame.rows; int thresholdPix = 0; double ratioReal; int thresholdValue = maxValue; for (; thresholdValue >= 0; thresholdValue--) { thresholdPix += p[thresholdValue]; ratioReal = (double)thresholdPix / countPix; if (ratioReal>ratio) { break; } } //可能图像不符合要求 if (thresholdValue < 0) { return -1; } //使用阈值将图像二值化 int curValue = 0; for (int i = 0; i < Frame.rows; ++i) { for (int j = 0; j < Frame.cols; ++j) { curValue = Frame.at<uchar>(i, j); if (curValue >= thresholdValue && curValue <= maxValue) { Frame.at<uchar>(i, j) = valueTop; } else { Frame.at<uchar>(i, j) = 0; } } } return 0; } int main(int argc, char* argv[]) { //读取灰度图像 Mat src = imread("F:/Bar/晶粒模板/F31.bmp", 0); if (src.empty()) { cout << "no such file or dictionary!" << endl; return -1; } //拷贝图像 Mat dst = src.clone(); //二值化 int res = pValueThreshold(dst, 240, 0.26346, 255); if (-1 == res) { cout << "ERROR: pValueThreshold" << endl; } //显示图像 imshow("gray", src); imshow("binary", dst); //等待按键 waitKey(0); return 0; }