您的位置:首页 > 运维架构

Derek Bradley & Gerhard Roth自适应二值化算法, Opencv实现

2015-10-17 20:41 459 查看

算法介绍请参考: http://blog.csdn.net/qiaxi/article/details/49205135

二值化函数:

void BradleyThreshold(const cv::Mat& srcImage, cv::Mat& dstImage, int iRows, int iCols, double dPercentThres)
{
// Safety check
if (srcImage.empty())
{
std::cout << "Empty matrix. Please check your input into QxBinarior::BradleyThreshold()" << std::endl << std::endl;
return;
}

if (dPercentThres < -1)
{
std::cout << "Percentage value can not be lhigher than 100% percent. Please check your input into QxBinarior::BradleyThreshold()" << std::endl << std::endl;
return;
}

// Deal with even blocksize
if (iRows % 2 == 0)
++iRows;
if (iCols % 2 == 0)
++iCols;

// Deal with color image
cv::Mat grayImg;
if (srcImage.type() == CV_8UC3)
cv::cvtColor(srcImage, grayImg, CV_BGR2GRAY);
else
grayImg = srcImage;

//-- Generate integral image.
int iRowAmount = grayImg.rows + 1; //Padding
int iColAmount = grayImg.cols + 1; //Padding
double* pIntegralImage = new double[iRowAmount * iColAmount];

// Zero padding
for (int iCol = 0; iCol < iColAmount; ++iCol)
pIntegralImage[iCol] = 0.0;
for (int iRow = 0; iRow < iRowAmount; ++iRow)
pIntegralImage[iRow*iColAmount] = 0.0;

bool bIsFirstRow = true;
double dSum = 0.0;
uchar* pCurrentRow = grayImg.ptr<uchar>(0);
for (int iRow = 1; iRow < iRowAmount; ++iRow)
{
bIsFirstRow = iRow == 1;
pCurrentRow = grayImg.ptr<uchar>(iRow - 1);
dSum = 0.0;
for (int iCol = 1; iCol < iColAmount; ++iCol)
{
dSum += pCurrentRow[iCol - 1];
if (!bIsFirstRow)
pIntegralImage[iRow*iColAmount + iCol] = dSum + pIntegralImage[(iRow - 1)*iColAmount + iCol];
else
pIntegralImage[iRow*iColAmount + iCol] = dSum;
}
}
//-- Integral image generated.

// Binarization
const int iRowHalfRange = iRows / 2;
const int iColHalfRange = iCols / 2;
int iRowBegin = 0;
int iRowEnd = 0;
int iColBegin = 0;
int iColEnd = 0;
int iArea = 0;
double dAverageVal = 0.0;
dstImage.create(grayImg.size(), CV_8UC1);
for (int iRow = 1; iRow < iRowAmount; ++iRow)
{
uchar* pDstCurrentRow = dstImage.ptr<uchar>(iRow - 1);
uchar* pSrcCurrentRow = grayImg.ptr<uchar>(iRow - 1);

iRowBegin = iRow - iRowHalfRange - 1;
iRowEnd = iRow + iRowHalfRange;
if (iRowBegin < 0)
iRowBegin = 0;
if (iRowEnd >= iRowAmount)
iRowEnd = iRowAmount - 1;
for (int iCol = 1; iCol < iColAmount; ++iCol)
{
iColBegin = iCol - iColHalfRange - 1;
iColEnd = iCol + iColHalfRange;
if (iColBegin < 0)
iColBegin = 0;
if (iColEnd >= iColAmount)
iColEnd = iColAmount - 1;
iArea = (iColEnd - iColBegin)*(iRowEnd - iRowBegin);
dSum = pIntegralImage[iRowBegin*iColAmount + iColBegin] + pIntegralImage[iRowEnd*iColAmount + iColEnd]
- pIntegralImage[iRowBegin*iColAmount + iColEnd] - pIntegralImage[iRowEnd*iColAmount + iColBegin];
dAverageVal = dSum / iArea;
pDstCurrentRow[iCol - 1] = pSrcCurrentRow[iCol - 1] > dAverageVal*(1 + dPercentThres) ? 255: 0;
}
}

delete[] pIntegralImage;
}


主函数:

#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>

#include "BradleyThreshold.h"

using namespace cv;
using namespace std;

int main()
{
Mat matSrcImage = imread("E:\\Picture\\IMG\\191.jpg", CV_LOAD_IMAGE_COLOR);

Mat matBinaryImage;
BradleyThreshold(matSrcImage, matBinaryImage, 5, 5, 0.1);  //参数需根据不同图片设定

imshow("Original Image", matSrcImage);
imshow("Bianry Image", matBinaryImage);
waitKey(0);

return EXIT_SUCCESS;
}

程序运行结果如下:









--------------------------------------------------------------------------------------------------------

测试发现,这段代码的时间效率比Opencv自带函数adpativeThreshold()低30%-50%, 二值化表现各有千秋。

对于整体较明亮的图像,adaptiveThreshold()的表现优于以上代码。但是对于整体灰度值偏低的图片,上面那段代码的表现要优于

adaptiveThreshold(). 平均灰度越低,这种差异越明显。因此推荐大家对昏暗的图片采用Bradley与Roth提出的该算法,明亮图片采用adaptiveThreshold()函数。

adaptiveThreshold()与上述算法最大的区别在于: adaptiveThreshold()以灰度值的差值的绝对数值为参数区分前景与背景,该算法则使用差值与本身灰度值的百分比作为区分依据,因此后者在灰度值整体偏低的情况下更有区分性。大家也可以对这个算法做一些改进,比如在二值化操作之前先对更大邻域内的图像均值做一次评估,若均值偏高则使用绝对数值差异作为二值化依据;如果均值偏低则使用差值百分比参数作为区分依据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息