Basic operations with images
Accessing pixel intensity values
为了获得像素强度值,您必须知道图像的类型和通道数。 以下是单通道灰度图像(8UC1 型)和像素坐标 x 和 y 的示例:
Scalar intensity = img.at<uchar>(y, x);
仅限 C++ 版本:intensity.val[0] 包含从 0 到 255 的值。注意 x 和 y 的顺序。 由于在 OpenCV 中图像由与矩阵相同的结构表示,因此我们对两种情况使用相同的约定——从 0 开始的行索引(或 y 坐标)在前,从 0 开始的列索引(或 x 坐标)在后 它。 或者,您可以使用以下表示法(仅限 C++):
Scalar intensity = img.at<uchar>(Point(x, y));
现在让我们考虑一个具有 BGR 颜色排序的 3 通道图像(imread 返回的默认格式):
C++ code
Vec3b intensity = img.at<Vec3b>(y, x);
uchar blue = intensity.val[0];
uchar green = intensity.val[1];
uchar red = intensity.val[2];
Python Python
_blue = img[y,x,0]
_green = img[y,x,1]
_red = img[y,x,2]
您可以对浮点图像使用相同的方法(例如,您可以通过在 3 通道图像上运行 Sobel 来获得这样的图像)(仅限 C++):
Vec3f intensity = img.at<Vec3f>(y, x);
float blue = intensity.val[0];
float green = intensity.val[1];
float red = intensity.val[2];
可以使用相同的方法来改变像素强度:
img.at<uchar>(y, x) = 128;
OpenCV 中有一些函数,尤其是来自 calib3d 模块的函数,例如 cv::projectPoints,它们采用 Mat 形式的 2D 或 3D 点数组。 矩阵应该只包含一列,每一行对应一个点,矩阵类型应该是 32FC2 或 32FC3 对应。 这样的矩阵可以很容易地从 std::vector 构造(仅限 C++):
vector<Point2f> points;
//... fill the array
Mat pointsMat = Mat(points);
可以使用相同的方法 Mat::at(仅限 C++)访问此矩阵中的一个点:
Point2f point = pointsMat.at<Point2f>(i, 0);
Memory management and reference counting
内存管理和引用计数
Mat 是一种保持矩阵/图像特征(行数和列数、数据类型等)和指向数据的指针的结构。 因此,没有什么能阻止我们拥有多个 Mat 实例对应相同的数据。 Mat 保留一个引用计数,该计数告诉当 Mat 的特定实例被销毁时是否必须释放数据。 下面是创建两个矩阵而不复制数据的示例(仅限 C++):
std::vector<Point3f> points;
// .. fill the array
Mat pointsMat = Mat(points).reshape(1);
结果,我们得到了一个 32FC1 的 3 列矩阵,而不是 32FC3 的 1 列矩阵。 pointsMat 使用来自点的数据,并且在销毁时不会释放内存。 然而,在这种特殊情况下,开发人员必须确保点的生命周期长于 pointsMat 如果我们需要复制数据,可以使用例如 cv::Mat::copyTo 或 cv::Mat::clone:
Mat img = imread("image.jpg");
Mat img1 = img.clone();
可以为每个函数提供一个空的输出 Mat。 每个实现都为目标矩阵调用 Mat::create。 如果矩阵为空,则此方法为矩阵分配数据。 如果它不为空并且具有正确的大小和类型,则该方法不执行任何操作。 但是,如果大小或类型与输入参数不同,则数据将被释放(并丢失)并分配新数据。 例如:
Mat img = imread("image.jpg");
Mat sobelx;
Sobel(img, sobelx, CV_32F, 1, 0);
Primitive operations
在矩阵上定义了许多方便的运算符。 例如,下面是我们如何从现有的灰度图像 img 制作黑色图像
img = Scalar(0);
选择感兴趣的区域:ROI
Rect r(10, 10, 100, 100);
Mat smallImg = img(r);
从彩色到灰度的转换:
Mat img = imread("image.jpg"); // loading a 8UC3 image
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
将图像类型从 8UC1 更改为 32FC1:
src.convertTo(dst, CV_32F);
Visualizing images
在开发过程中查看算法的中间结果非常有用。 OpenCV 提供了一种可视化图像的便捷方式。 可以使用以下方式显示 8U 图像:
Mat img = imread("image.jpg");
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", img);
waitKey();
对 waitKey() 的调用启动了一个消息传递循环,该循环等待“图像”窗口中的击键。 一张 32F 的图像需要转换成 8U 类型。 例如:
Mat img = imread("image.jpg");
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
Mat sobelx;
Sobel(grey, sobelx, CV_32F, 1, 0);// Sobel算子是通过离散微分方法求取图像边缘的边缘检测算子,
double minVal, maxVal;
minMaxLoc(sobelx, &minVal, &maxVal); //找到最小和最大强度find minimum and maximum intensities
Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", draw);
waitKey();
这里 cv::namedWindow 不是必需的,因为它紧随其后的是 cv::imshow。 不过,它可用于更改窗口属性或使用 cv::createTrackbar
参考
作用 OpenCV Sobel函数_翟羽嚄的博客-优快云博客_opencv sobel
【OpenCV 4开发详解】Sobel算子 - 知乎 (zhihu.com)
Sobel函数使用扩展的 Sobel 算子,来计算一阶、二阶、三阶或混合图像差分。
Sobel算子是通过离散微分方法求取图像边缘的边缘检测算子,其求取边缘的思想原理与我们前文介绍的思想一致,除此之外Sobel算子还结合了高斯平滑滤波的思想,将边缘检测滤波器尺寸由ksize * 1改进为ksize * ksize,提高了对平缓区域边缘的响应,相比前文的算法边缘检测效果更加明显。使用Sobel边缘检测算子提取图像边缘的过程大致可以分为以下三个步骤:
- Step1:提取X方向的边缘, X 方向一阶Sobel边缘检测算子如(5.16)所示。
- Step2:提取Y方向的边缘, Y 方向一阶Sobel边缘检测算子如(5.17)所示。
Step3:综合两个方向的边缘信息得到整幅图像的边缘。由两个方向的边缘得到整体的边缘有两种计算方式:第一种是求取两张图像对应像素的像素值的绝对值之和;第二种是求取两张图像对应像素的像素值的平方和的二次方根。这两种计算方式在式(5.18)给出。
OpenCV 4提供了对图像提取Sobel边缘的Sobel()函数,该函数的函数原型
Sobel()函数原型
void cv::Sobel(InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
int ksize = 3,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
src:待提取边缘的图像,
dst:输出图像,与输入图像src具有相同的尺寸和通道数,数据类型由第三个参数ddepth控制。
ddepth:输出图像的数据类型(深度),根据输入图像的数据类型不同拥有不同的取值范围,具体的取值范围在表5-1给出,当赋值为-1时,输出图像的数据类型自动选择。
dx:X方向的差分阶数
dy:Y方向的差分阶数
ksize:Sobel边缘算子的尺寸,必须是1、3、5或者7。
scale:对导数计算结果进行缩放的缩放因子,默认系数为1,不进行缩放。
delta:偏值,在计算结果中加上偏值。
borderType:像素外推法选择标志,取值范围在表3-5中给出,默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。
该函数的使用方式与分离卷积函数sepFilter2D()相似,函数的前两个参数分别为输入图像和输出图像,第三个参数为输出图像的数据类型,这里需要注意由于提取边缘信息时有可能会出现负数,因此不要使用CV_8U数据类型的输出图像,与Sobel算子方向不一致的边缘梯度会在CV_8U数据类型中消失,使得图像边缘提取不准确。函数中第三个、第四个和第五个参数是控制图像边缘检测效果的关键参数,这三者存在的关系是任意一个方向的差分阶数都需要小于滤波器的尺寸,特殊情况是当ksize=1时,任意一个方向的阶数需要小于3。一般情况中,差分阶数的最大值为1时,滤波器尺寸选3;差分阶数的最大值为2时,滤波器尺寸选5;差分阶数最大值为3时,滤波器尺寸选7。当滤波器尺寸ksize=1时,程序中使用的滤波器尺寸不再是正方形,而是3×1或者1×3。最后三个参数为图像放缩因子、偏移量和图像外推填充方法的标志,多数情况下并不需要设置,只需要采用默认参数即可。
为了更好的理解Sobel()函数的使用方法,在代码清单5-25中给出了利用Sobel()函数提取图像边缘的示例程序,程序中分别提取X方向和Y方向的1阶边缘,并利用两个方向的边缘求取整幅图像的边缘,程序运行结果如图5-30所示。
调用Sobel函数
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
const char* load_win = "load image";
const char* gray_win = "gray imgae";
int main(int argc, char** argv)
{
Mat src, dst;
src = imread("2.jpg");
if (src.empty())
{
cout << "could not load..." << endl;
return -1;
}
namedWindow(load_win, 0);
imshow(load_win, src);
GaussianBlur(src, dst, Size(3, 3), 0, 0);
Mat src_gray;
cvtColor(dst, src_gray, COLOR_BGR2GRAY);
namedWindow(gray_win, 0);
imshow(gray_win, src_gray);
Mat x_grad, y_grad;
Sobel(src_gray, x_grad, CV_16S, 1, 0, 3);
Sobel(src_gray, y_grad, CV_16S, 0, 1, 3);
convertScaleAbs(x_grad, x_grad);
convertScaleAbs(y_grad, y_grad);
namedWindow("x_grad", 0);
imshow("x_grad", x_grad);
namedWindow("y_grad", 0);
imshow("y_grad", y_grad);
waitKey(0);
return 0;
}
# openCV中convertTo的用法
openCV中convertTo的用法_猫猫与橙子的博客-优快云博客_convertto opencv
convertTo的用法
src.convertTo(dst, type, scale, shift)
img参数为图像数据来源,其类型为Mat。
注意也不是所有格式的Mat型数据都能被使用保存为图片,目前OpenCV主要只支持单通道和3通道的图像,并且此时要求其深度为8bit和16bit无符号(即CV_16U),所以其他一些数据类型是不支持的,比如说float型等。
如果Mat类型数据的深度和通道数不满足上面的要求,则需要使用convertTo()函数和cvtColor()函数来进行转换。
convertTo()函数负责转换数据类型不同的Mat,即可以将类似float型的Mat转换到imwrite()函数能够接受的类型。
而cvtColor()函数是负责转换不同通道的Mat,因为该函数的第4个参数就可以设置目的Mat数据的通道数(只是我们一般没有用到它,一般情况下这个函数是用来进行色彩空间转换的)。
另外也可以不用imwrite()函数来存图片数据,可以直接用通用的XML IO接口函数将数据存在XML或者YXML中。
缩放并转换到另外一种数据类型:
dst:目的矩阵;
type:需要的输出矩阵类型,或者更明确的,是输出矩阵的深度,如果是负值(常用-1)则输出矩阵和输入矩阵类型相同;
scale:比例因子;
shift:将输入数组元素按比例缩放后添加的值;
dst(i)=src(i)xscale+(shift,shift,...)
如果scale=1,shift=0,则不进行比例缩放。
如果输入数组与输出数组的类型相同,则函数可以被用于缩放和平移矩阵或图像;
opencv——convertTo_yangjianqiao0的博客-优快云博客
函数原型
void Mat::convertTo( Mat& m, int rtype, double alpha=1, double beta=0 )
const;
输入参数:
m 目标矩阵。如果m的大小与原矩阵不一样,或者数据类型与参数不匹配,那么在函数convertTo内部会先给m重新分配空间。
rtype 指定从原矩阵进行转换后的数据类型,即目标矩阵m的数据类型。当然,矩阵m的通道数应该与原矩阵一样的。如果rtype是负数,那么m矩阵的数据类型应该与原矩阵一样。
alpha 缩放因子。默认值是1。即把原矩阵中的每一个元素都乘以alpha。
beta 增量。默认值是0。即把原矩阵中的每一个元素都乘以alpha,再加上beta。
功能
把一个矩阵从一种数据类型转换到另一种数据类型,同时可以带上缩放因子和增量,公式如下:
m(x,y)=saturate_cast<rType>(alpha*(*this)(x,y)+beta);
由于有数据类型的转换,所以需要用saturate_cast<rType>来处理数据的溢出。