内容
- 掩膜操作解释
- 获取图像像素指针
- 完整代码
1. 掩膜操作解释
- 掩膜操作可实现图像对比度调整
- 矩阵的掩膜操作是根据掩膜来重新计算每个像素的像素值,掩膜(mask 也被称为 Kernel)
例如:
- 红色是中心像素,从上到下,从左到右对每个像素都做同样的处理操作,得到最终结果就是对比度提高之后的输出图像
I(i,j) = 5 * I(i,j) - [I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]
通过公式可知,如果选择的这个中心点比周围的点暗,那就让它更暗,同理,如果比周围亮,那就让它更亮。当把这个操作扩展到整个图像之后,图像之间的亮暗差距会变得更大,也就是对比度会更大。又因为一个图像的边缘点没有四周进行对比,不能使用公式,所以在写程序时,循环变量即图像的行数和列数要从 1 开始,去掉最外圈像素没有办法进行比较的点。
2. 获取图像像素指针
2.1 CV_Assert
CV_Assert(myImage.depth() == CV_8U);
- Mat.ptr(int i=0) 获取像素矩阵的指针,索引 i 表示第几行,从 0 开始计行数。“ptr” 是指针的意思。“uchar” 是一个字节。
- 获得当前行指针
const uchar* current = myImage.ptr<uchar>(row);
- 获取当前像素点 P(row, col) 的像素值 p(row, col) = current[col];
2.2 saturate_cast 像素范围处理
- 像素范围处理 saturate_cast,这个函数的功能是确保 RGB 值的范围在 0~255 之间。
语句 | 作用 |
saturate_cast (-100) | 返回 0 |
saturate_cast (288) | 返回255 |
saturate_cast (100) | 返回100 |
2.3 获取图像长度和宽度
图像的长度和宽度即图像的行数 rows 和列数 cols ,这些都是 Mat 对象的属性,可直接通过 Mat 对象访问。
image.rows // 图像行数
image.cols // 图像列数
注意:以上方式与其说是计算行数和列数不如说是计算图像的行数和列通道数,而三通道的图像存储第一行为 B0G0R0B1G1R1……,不是 B0B1B2……,所以在计算 BGR 三通道图像的列数时要 *3,即乘以图像的通道数:
int cols = image.cols * image.channels();
2.4 掩膜操作
有两种实现掩膜操作的方法
2.4.1 方法一
使用循环,手动编写中心元素的计算公式,然后遍历整幅图像的所有中心点。
完整代码
#include <opencv.hpp>
#include <iostream>
int main(int argc, char** argv)
{
cv::String imageName = "HappyFish.jpg";
if (argc > 1)
imageName = argv[1];
cv::Mat image = cv::imread(cv::samples::findFile(imageName), cv::IMREAD_COLOR);
cv::namedWindow("Display", cv::WINDOW_AUTOSIZE);
if (image.empty())
{
std::cout << "No image" << std::endl;
return -1;
}
cv::imshow("Display", image);
cv::Mat dst;
int cols = (image.cols - 1) * image.channels();
int offsetx = image.channels();
int rows = image.rows;
dst = cv::Mat::zeros(image.size(), image.type());
for (int row = 1; row < rows - 1; row++)
{
const uchar* previous = image.ptr<uchar>(row - 1);
const uchar* current = image.ptr<uchar>(row);
const uchar* next = image.ptr<uchar>(row + 1);
uchar* output = dst.ptr<uchar>(row);
for (int col = 1; col < cols - 1; col++)
{
output[col] = cv::saturate_cast<uchar>(5 * current[col] - (previous[col] + next[col] + current[col - offsetx] + current[col + offsetx]));
}
}
cv::namedWindow("Transform", cv::WINDOW_AUTOSIZE);
imshow("Transform", dst);
cv::waitKey(0);
return 0;
}
结果
2.4.2 方法二
先建立一个掩膜模板,使用 filter2D 语句。
filter2D
- 定义掩膜:
cv::Mat mask = (cv::Mat_<char>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
- filter2D(src, dst, src.depth(), kernel); 其中 src 与 dst 是 Mat 类型变量,src.depth() 表示位图深度,有 32、24、8 等。若 src.depth() 赋值为 -1 时,意味着图像深度与原图一致。
完整代码
#include <opencv.hpp>
#include <iostream>
int main(int argc, char** argv)
{
cv::String imageName = "HappyFish.jpg";
if (argc > 1)
imageName = argv[1];
cv::Mat image = cv::imread(cv::samples::findFile(imageName), cv::IMREAD_COLOR);
cv::namedWindow("Display", cv::WINDOW_AUTOSIZE);
if (image.empty())
{
std::cout << "No image" << std::endl;
return -1;
}
cv::imshow("Display", image);
cv::Mat dst;
cv::Mat mask = (cv::Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cv::filter2D(image, dst, image.depth(), mask);
cv::namedWindow("Transform", cv::WINDOW_AUTOSIZE);
imshow("Transform", dst);
cv::waitKey(0);
return 0;
}
结果