二值化是图像分割的一种方法,即将256个亮度等级的灰度图像通过适当的阈值选取,使得所有小于该阈值的像素置为一个值,所有大于该阈值的像素置为另一个值,最终得到一张非黑即白的二值图像。可见阈值的选择非常重要,不同的阈值,运算后得到的结果图像是完全不同的。对于多张图像,如何选取每一张图像合适的阈值呢?采用主观判定显然不太现实,合理的选择应该是采用一种自适应的阈值计算方法。自适应阈值计算,我主要看了大津法(维基百科),即最大类间方差法,由日本学者大津展之于1979年提出,简称Otsu,算法基本思想是:设使用某一个阈值将灰度图像根据灰度大小,分成前景部分和背景部分,在这两部分“类间差异最大”(也即“类内差异最小”)的时候,得到的阈值即是最优的二值化阈值。算法原理维基百科讲的很详细,这里不再赘述,只是配的程序实在不敢恭维,或许也就计算机能明白。
大津法c++实现:
#include "stdafx.h" #include <cv.h> #include <cxcore.h> #include <highgui.h> using namespace cv; // 大津法求阈值 uchar OtsuThreshold(Mat &imgGray) { int width = imgGray.cols; int height = imgGray.rows; int histData[256] = { 0 }; // 直方图统计 uchar *pImg = imgGray.data; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { histData[pImg[j]]++; } pImg += imgGray.step; } // 计算像素总数及灰度总和 int totalCount = width * height; double graySum = 0.0; for (int i = 0; i < 256; i++) { graySum += i * histData[i]; } uchar threshold = 0; int backCount = 0, foreCount = 0; double backGraySum = 0.0, varMax = -999.9; for (int i = 0; i < 256; i++) { // 背景像素数 backCount += histData[i]; if (backCount == 0) { continue; } // 前景像素数 foreCount = totalCount - backCount; if (foreCount == 0) { break; } // 背景像素灰度总和 backGraySum += double(i * histData[i]); // 背景/前景均值 double backMean = backGraySum / backCount; double foreMean = (graySum - backGraySum) / foreCount; // 计算类间差异 double varBetween = backCount * foreCount * (backMean - foreMean) * (backMean - foreMean); // 最大值位置,即阈值 if (varBetween > varMax) { varMax = varBetween; threshold = i; } } return threshold; } int main(int argc, _TCHAR* argv[]) { Mat img = imread("image\\snow.jpg"); imshow("src", img); // 计算阈值 Mat imgGray; cvtColor(img, imgGray, CV_BGR2GRAY); uchar threshold = OtsuThreshold(imgGray); // 二值化 int width = imgGray.cols; int height = imgGray.rows; uchar *pImg = imgGray.data; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { pImg[j] = (pImg[j] > threshold) ? 255 : 0; } pImg += imgGray.step; } imshow("result", imgGray); waitKey(); return 0; }
运行结果:
工程下载链接:http://download.csdn.net/detail/u013085897/6774265
程序基于vs2005 + opencv210实现,下载工程后,如果与自己使用的opencv版本不一致,则需要对工程进行简单配置才能正确运行。