4.opencv由浅入深–图像像素相关操作
1.图像基础
1、模拟图像:连续图像 通过某种物理量(如光、电等)的强弱变化来记录图像亮度信息,是连续变化的。
2、数字图像:计算机采用0/1编码的系统,数字图像也是利用0/1来记录信息,我们平常接触的图像都是8位数图像包含0-255灰度。像素点是组成数字图像的最小单位,对于不同类型的图像,像素点数据不一样。
数字图像分类:二值图像、灰度图像、彩色图像。
二值图像:每个像素取值0/1。
灰度图像:每个像素8位 0-255 (没有三个通道,只有一个通道,表示亮度就可以)。
彩色图像:每个像素由R G B 三个分量表示,每个分量0-255,因此单独拿出一个分量就是一个灰度图,每一像素的颜色需由R,G、B三个分量来表示,M,N分别表示图像的行列数,三个MxN的二维矩阵分别表示各个像素的R,G、B三个颜色分量。
2.opencv内部定义的数据类型
在操作像素点之前,先熟悉一下opencv定义常用数据类型
在include\opencv2\core\types.hpp文件中
typedef Point_<int> Point2i;
typedef Point_<int64> Point2l;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Point2i Point;
typedef Point3_<int> Point3i;
typedef Point3_<float> Point3f;
typedef Point3_<double> Point3d;
typedef Size_<int> Size2i;
typedef Size_<int64> Size2l;
typedef Size_<float> Size2f;
typedef Size_<double> Size2d;
typedef Size2i Size;
typedef Rect_<int> Rect2i;
typedef Rect_<float> Rect2f;
typedef Rect_<double> Rect2d;
typedef Rect2i Rect;
typedef Scalar_<double> Scalar;
在modules\core\include\opencv2\core\matx.hpp文件中
typedef Matx<float, 1, 2> Matx12f;
typedef Matx<double, 1, 2> Matx12d;
typedef Matx<float, 1, 3> Matx13f;
typedef Matx<double, 1, 3> Matx13d;
typedef Matx<float, 1, 4> Matx14f;
typedef Matx<double, 1, 4> Matx14d;
typedef Matx<float, 1, 6> Matx16f;
typedef Matx<double, 1, 6> Matx16d;
typedef Matx<float, 2, 1> Matx21f;
typedef Matx<double, 2, 1> Matx21d;
typedef Matx<float, 3, 1> Matx31f;
typedef Matx<double, 3, 1> Matx31d;
typedef Matx<float, 4, 1> Matx41f;
typedef Matx<double, 4, 1> Matx41d;
typedef Matx<float, 6, 1> Matx61f;
typedef Matx<double, 6, 1> Matx61d;
typedef Matx<float, 2, 2> Matx22f;
typedef Matx<double, 2, 2> Matx22d;
typedef Matx<float, 2, 3> Matx23f;
typedef Matx<double, 2, 3> Matx23d;
typedef Matx<float, 3, 2> Matx32f;
typedef Matx<double, 3, 2> Matx32d;
typedef Matx<float, 3, 3> Matx33f;
typedef Matx<double, 3, 3> Matx33d;
typedef Matx<float, 3, 4> Matx34f;
typedef Matx<double, 3, 4> Matx34d;
typedef Matx<float, 4, 3> Matx43f;
typedef Matx<double, 4, 3> Matx43d;
typedef Matx<float, 4, 4> Matx44f;
typedef Matx<double, 4, 4> Matx44d;
typedef Matx<float, 6, 6> Matx66f;
typedef Matx<double, 6, 6> Matx66d;
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;
typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;
typedef Vec<ushort, 2> Vec2w;
typedef Vec<ushort, 3> Vec3w;
typedef Vec<ushort, 4> Vec4w;
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;
typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;
3.opencv中像素操作
像素点在不同图像中表示方式不一样,分为单通道和多通道。单通道中像素点是一个无符号的8位整数,多通道中像素点由每个通道组成的向量,每个通道是一个无符号的8位整数。
opencv有3种方式可以操作像素:
1、用at遍历图像元素,即为通过成员函数at访问。
使用at方法访问元素需要提供行号和列号,以及元素的类型.
// 单通道图像
img.at<uchar>(rows, cols) = 200;
// 三通道图像
img.at<Vec3b>(rows, cols) = Vec3b(100, 100, 100);
2、用指针遍历图像元素,通过成员函数ptr访问。
用指针遍历图像元素更加高效。
// 单通道图像
uchar* p = img.ptr<uchar>(rows, cols);
*p = 200;
// 三通道图像
Vec3b* p = img.ptr<Vec3b>(rows, cols);
p[0] = 100;
p[1] = 100;
p[2] = 100;
3、用迭代器变量图像像素。
// 单通道图像
Mat_<uchar>::iterator begin = img.begin<uchar>();
Mat_<uchar>::iterator end = img.end<uchar>();
for (auto it = begin; it != end; it++)
{
*it = 200;
}
// 三通道图像
Mat_<Vec3b>::iterator begin = img.begin<Vec3b>();
Mat_<Vec3b>::iterator end = img.end<Vec3b>();
for (auto it = begin; it != end; it++)
{
*it = Vec3b(200, 200, 200);
}
或者
// 单通道图像
MatIterator_<uchar> begin = img.begin<uchar>(), end = img.end<uchar>();
for (auto it = begin; it != end; it++)
{
*it = 200;
}
// 三通道图像
MatIterator_<Vec3b> begin = img.begin<Vec3b>(), end = img.end<Vec3b>();
for (auto it = begin; it != end; it++)
{
*it = Vec3b(200, 200, 200);
}
4.opencv像素操作实例
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
// 单通道图像 白色图像
Mat img1 = Mat(400, 500, CV_8UC1, Scalar(255));
// 三通道图像 白色图像
Mat img3 = Mat(400, 500, CV_8UC3, Scalar(255,255,255));
// 显示原始图像
imshow("单通道图像 原始图:", img1);
imshow("三通道图像 原始图:", img3);
// 1.通过at访问 修改为灰色图像
for (int rows = 0; rows < img1.rows; rows++)
{
for (int cols = 0; cols < img1.cols; cols++)
{
img1.at<uchar>(rows, cols) = 100;
}
}
imshow("单通道图像 灰色图:", img1);
for (int rows = 0; rows < img3.rows; rows++)
{
for (int cols = 0; cols < img3.cols; cols++)
{
img3.at<Vec3b>(rows, cols) = Vec3b(100, 100, 100);
}
}
imshow("三通道图像 灰色图:", img3);
// 2.通过指针ptr访问
for (int rows = 0; rows < img1.rows; rows++)
{
for (int cols = 0; cols < img3.cols; cols++)
{
uchar* p = img1.ptr<uchar>(rows, cols);
*p = 0;
}
}
imshow("单通道图像 黑色图:", img1);
for (int rows = 0; rows < img3.rows; rows++)
{
for (int cols = 0; cols < img3.cols; cols++)
{
Vec3b* p = img3.ptr<Vec3b>(rows, cols);
p[0] = 0;
p[1] = 0;
p[2] = 0;
}
}
imshow("三通道图像 黑色图:", img3);
// 3.通过迭代器访问
//Mat_<uchar>::iterator begin1 = img1.begin<uchar>();
//Mat_<uchar>::iterator end1 = img1.end<uchar>();
//for (auto it = begin1; it != end1; it++)
//{
// *it = 200;
//}
MatIterator_<uchar> begin1 = img1.begin<uchar>(), end1 = img1.end<uchar>();
for (auto it = begin1; it != end1; it++)
{
*it = 200;
}
imshow("单通道图像 灰白图:", img1);
//Mat_<Vec3b>::iterator begin3 = img3.begin<Vec3b>();
//Mat_<Vec3b>::iterator end3 = img3.end<Vec3b>();
//for (auto it = begin3; it != end3; it++)
//{
// *it = Vec3b(200, 200, 200);
//}
MatIterator_<Vec3b> begin3 = img3.begin<Vec3b>(), end3 = img3.end<Vec3b>();
for (auto it = begin3; it != end3; it++)
{
*it = Vec3b(200, 200, 200);
}
imshow("三通道图像 灰白图:", img3);
waitKey(0);
destroyAllWindows();
return 0;
}