题目:将灰度图像进行二值化,变为黑白图像。 采用国际标准测试图像Lena。 C++代码:
cv::Mat Image = cv::imread("Lena.bmp");
int width = Image.cols;
int height = Image.rows;
cv::Mat grayImage = cv::Mat::zeros(height, width, CV_8UC1);
//opencv灰度化
cv::cvtColor(Image, grayImage, CV_BGR2GRAY);
cv::Mat thresholdImage = cv::Mat::zeros(height, width, CV_8UC1);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (grayImage.at<uchar>(i, j) > 128)
{
thresholdImage.at<uchar>(i, j) = 255;
}
else
{
thresholdImage.at<uchar>(i, j) = 0;
}
}
}
这里有一点需要注意,标准测试图像Lena通过opencv读取进来是三通道的,因此我们要将其转为单通道图像,这里我们采用了cvtColor函数。 假如我们这里没有将其转为单通道图像,那么在下面的for循环中就会出现问题。 结果展示: 从代码中可以看出,阈值128是我们人为指定的,因此可能存在很大的误差。我们可以采用大津二值化算法自动的选取一副图像合适的阈值。 二值化在opencv中的函数实现如下:
double cv::threshold(InputArray src,
OutputArray dst,
double thresh,
double maxval,
int type
)
src:输入图像(单通道或者多通道,8位或者32位浮点类型) dst:输出图像,和输入图像大小相同,通道数也相同 thresh:阈值 type:采用的二值化方法 maxval:采用THRESH_BINARY和THRESH_BINARY_INV方法时的最大值 所有的type如下图所示: 注意:当使用Ostu’s和Triangle方法时,输入只能是8位单通道图像,且此时函数返回计算出的阈值。
在探讨大津二值化算法前,我们先陈述下opencv访问像素的几种方式。从快到慢排序如下:
- 使用ptr<>(row)访问图像像素
for (int row = 0; row < image.rows; row++)
{
cv::Vec3b *curRow = image.ptr<cv::Vec3b>(row);
for (int col = 0; col < image.cols; col++)
{
(*(curRow + col))[0] = 0;
(*(curRow + col))[1] = 0;
(*(curRow + col))[2] = 0;
}
}
for (int row = 0; row < image.rows; row++)
{
uchar *curRow = image.ptr<uchar>(row);
for (int col = 0; col < image.cols; col++)
{
*(curRow + col)= 0;
}
}
2.使用data结合step方式访问图像像素 成员函数step返回该Mat对象一行所占的数据字节数。比如说彩色图像的宽度是500,那么image.step返回值是1500。因为一行有500个像素点,每个像素点包括R、G、B三个通道,每一个通道在一个像素点占1个字节(8位),代表了0-255。 成员函数data:返回一个uchar类型的指针,指向Mat数据矩阵的首地址。
uchar *pData = image.data;
for (int row = 0; row < image.rows; row++)
{
for (int col = 0; col < image.cols; col++)
{
*(pData + row * image.step + col * image.channels() + 0) = 0;
*(pData + row * image.step + col * image.channels() + 1) = 0;
*(pData + row * image.step + col * image.channels() + 2) = 0;
}
}
3.使用ptr<>(row,col)访问图像像素
for (int row = 0; row < image.rows; row++)
{
for (int col = 0; col < image.cols; col++)
{
cv::Vec3b *pData = image.ptr<cv::Vec3b>(row, col);
(*pData)[0] = 0;
(*pData)[1] = 0;
(*pData)[2] = 0;
}
}
4.使用迭代器iterator访问像素,相比用指针直接访问像素可能出现越界问题,迭代器很安全
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itEnd = image.end<cv::Vec3b>();
for (; it != itEnd; it++)
{
(*it)[0] = 0;
(*it)[1] = 0;
(*it)[2] = 0;
}
5.使用at<>访问像素,如最开始的代码所示。 在下一节,我们讨论大津二值化算法的原理及具体代码实现。 参考博客:https://github.com/gzr2017/ImageProcessing100Wen https://blog.csdn.net/cutemypig/article/details/107505567 https://docs.opencv.org/3.4.10/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57
|