[OPENCV]003.核心功能

在这里,您将了解关于库的基本构建块。必须阅读并了解如何在像素级上操作图像。

Mat-基本的图像容器

您将了解如何在内存中存储映像,以及如何将其内容打印到控制台。

1.Mat不需要手动分配它的内存,也并在不需要释放

2.通过让两个Mat对象的矩阵指针指向相同的地址,来避免数据复制导致计算速度降低。赋值操作符和复制构造函数只复制指向矩阵的头,而不是数据本身。
Mat A,C;							//只创建标题部分
A = imread(argv[1], IMREAD_COLOR); //这里我们将知道使用的方法(分配矩阵)
Mat B (A);							//使用复制构造函数
C = A;								//赋值运算符
Mat D (A, Rect(10,10,100, 100));	//使用矩形
Mat D =A(Range::all(), Range(1,3));//使用行和列边界

3.可以使用cv::Mat::clone()和cv::Mat::copyTo()函数复制图像的底层矩阵
Mat F = A.clone();
Mat G;
A.copyTo(G);

4.可以使用Mat的<<运算符来显示二维矩阵
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;
CV_8UC3:CV_[8位][无符号的][类型前缀]C[通道号码]
5.使用C/ c++数组并通过构造函数进行初始化
int sz[3] = {2,2,2};
Mat L(3,sz, CV_8UC(1), Scalar::all(0));

M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " "  << M << endl << endl;

如何扫描图像,查找表和时间测量

您将了解如何使用OpenCV扫描图像(遍历每个图像像素)

我们将寻求以下问题的答案:
如何遍历图像的每一个像素?
如何存储OpenCV矩阵值?
如何衡量我们算法的性能?
什么是查询表,为什么使用它们?

通道的顺序是相反的:BGR而不是RGB
使用cv::Mat::isContinuous()函数来询问矩阵是否连续的方式存储;
矩阵掩码运算

您将了解如何使用邻居访问扫描图像,并使用cv::filter2D函数对图像应用内核过滤器。

操作的图片

从文件中读取/写入图像,访问像素,原始操作,可视化图像。

1.文件的读写
Mat img = imread(filename);
Mat img = imread(filename, IMREAD_GRAYSCALE);
文件的格式由它的内容(前几个字节)决定,保存一个图像到一个文件。文件的格式由其扩展名决定。使用cv::imdecode和cv::imencode读取/写入图像从/到内存,而不是一个文件。
imwrite(filename, img);


2.为了得到像素强度值,你必须知道图像的类型和通道的数量。下面是一个单通道灰度图像(类型8UC1)和像素坐标x和y的例子:
 Scalar intensity = img.at<uchar>(y, x);
 Scalar intensity = img.at<uchar>(Point(x, y));
3.一个BGR颜色顺序的3通道图像(imread返回的默认格式):
Vec3b intensity = img.at<Vec3b>(y, x);
uchar blue 	= intensity.val[0];
uchar green = intensity.val[1];
uchar red 	= intensity.val[2];
4.以Mat的形式取一个二维或三维点数组,矩阵只包含一列,每一行对应一个点,对应的矩阵类型为32FC2或32FC3     vector<Point2f> points;
Mat pointsMat = Mat(points);
5.使用相同的方法可以访问矩阵中的一个点:
Point2f point = pointsMat.at<Point2f>(i, 0);
6.得到了一个3列的32FC1矩阵,而不是一个1列的32FC3矩阵
std::vector<Point3f> points;
Mat pointsMat = Mat(points).reshape(1);
7.复制        
Mat img = imread("image.jpg");
Mat img1 = img.clone();

8.例如,下面是我们如何从现有的灰度图像img中创建一个黑色图像
img = Scalar(0);
9.选择感兴趣的地区:
Rect r(10, 10, 100, 100);
Mat smallImg = img(r);
10.从彩色到灰度的转换:
Mat img = imread("image.jpg"); 
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
11.图像类型由8UC1改为32FC1:
src.convertTo(dst, CV_32F);

12.图像可视化
Mat img = imread("image.jpg");
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", img);
waitKey();

Mat img = imread("image.jpg");
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
Mat sobelx;
Sobel(grey, sobelx, CV_32F, 1, 0);
double minVal, maxVal;

minMaxLoc(sobelx, &minVal, &maxVal); //找出最小和最大强度

Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", draw);
waitKey();
添加(混合)两个图像

将学习如何混合两个图像!

在本教程中,您将学习:
什么是线性混合,为什么它有用?
如何使用addWeighted()添加两个图像
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cin;
using std::cout;
using std::endl;
int main( void )
{
   double alpha = 0.5; double beta; double input;
   Mat src1, src2, dst;
   cout << " Simple Linear Blender " << endl;
   cout << "-----------------------" << endl;
   cout << "* Enter alpha [0.0-1.0]: ";
   cin >> input;
   // We use the alpha provided by the user if it is between 0 and 1
   if( input >= 0 && input <= 1 )
     { alpha = input; }
   src1 = imread( samples::findFile("LinuxLogo.jpg") );
   src2 = imread( samples::findFile("WindowsLogo.jpg") );
   if( src1.empty() ) { cout << "Error loading src1" << endl; return EXIT_FAILURE; }
   if( src2.empty() ) { cout << "Error loading src2" << endl; return EXIT_FAILURE; }
   beta = ( 1.0 - alpha );
   addWeighted( src1, alpha, src2, beta, 0.0, dst);
   imshow( "Linear Blend", dst );
   waitKey(0);
   return 0;
}

使用imread读取图片,addWeighted进行混合叠加,src1和src2图片的尺寸和类型一致,alpha为叠加前景的透明度,调用addWeighted实现叠加
改变图像的对比度和亮度

将学习如何改变我们的形象外观!

在本教程中,您将学习如何:
获取像素值
用零初始化一个矩阵
了解cv::saturate_cast的作用,及它为什么有用
获取一些关于像素变换的很酷的信息
提高图像亮度的实例
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cin;
using std::cout;
using std::endl;
using namespace cv;
int main( int argc, char** argv )
{
    CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
    Mat image = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( image.empty() )
    {
      cout << "Could not open or find the image!\n" << endl;
      cout << "Usage: " << argv[0] << " <Input image>" << endl;
      return -1;
    }
    Mat new_image = Mat::zeros( image.size(), image.type() );
    double alpha = 1.0; /*< Simple contrast control */
    int beta = 0;       /*< Simple brightness control */
    cout << " Basic Linear Transforms " << endl;
    cout << "-------------------------" << endl;
    cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;
    cout << "* Enter the beta value [0-100]: ";    cin >> beta;
    for( int y = 0; y < image.rows; y++ ) {
        for( int x = 0; x < image.cols; x++ ) {
            for( int c = 0; c < image.channels(); c++ ) {
                new_image.at<Vec3b>(y,x)[c] =
                  saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );
            }
        }
    }
    imshow("Original Image", image);
    imshow("New Image", new_image);
    waitKey();
    return 0;
}
imread读取原图片
通过 Mat::zeros( image.size(), image.type() )创建和原图片尺寸和类型一致的新图片设置对比度和亮度
通过new_image.at<Vec3b>(y,x)[c]设置每一个像素点的值;
离散傅里叶变换

你会看到如何以及为什么在OpenCV中使用离散傅里叶变换。

我们将寻求以下问题的答案:
什么是傅里叶变换,为什么要用它?
在OpenCV中怎么做?
使用诸如:copyMakeBorder()、merge()、dft()、getOptimalDFTSize()、log()和normalize()等函数。

#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
static void help(char ** argv)
{
    cout << endl
        <<  "This program demonstrated the use of the discrete Fourier transform (DFT). " << endl
        <<  "The dft of an image is taken and it's power spectrum is displayed."  << endl << endl
        <<  "Usage:"                                                                      << endl
        << argv[0] << " [image_name -- default lena.jpg]" << endl << endl;
}
int main(int argc, char ** argv)
{
    help(argv);
    const char* filename = argc >=2 ? argv[1] : "lena.jpg";
    Mat I = imread( samples::findFile( filename ), IMREAD_GRAYSCALE);
    if( I.empty()){
        cout << "Error opening image" << endl;
        return EXIT_FAILURE;
    }
    //DFT的性能取决于图像的大小,对于2、3和5的倍数的图像大小,它往往是最快的。因此,为了获得最大的性能,通常将边界值填充到图像中,得到具有特征的大小。
    Mat padded;                           
    //获取最佳大小
    int m = getOptimalDFTSize( I.rows );
    int n = getOptimalDFTSize( I.cols ); 
    //扩展图像的边界(附加的像素被初始化为零)
    copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
    //让复杂的和真实的价值都有一席之地,傅里叶变换的结果是复数的。这意味着对于每个图像值,结果是两个图像值(每个组件一个)。此外,它的频域范围要比它的空间对应物大得多。因此,我们通常至少以浮点格式存储它们。因此,我们将把输入图像转换为这种类型,然后用另一个通道展开它,以保存复数值:
    Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
    Mat complexI;
    merge(planes, 2, complexI);         // 在展开的平面上加上另一个零
    //进行离散傅里叶变换
    dft(complexI, complexI);          
// 计算大小和切换到对数尺度
    // => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
    split(complexI, planes);                   // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
    magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
    Mat magI = planes[0];
    magI += Scalar::all(1);                    // switch to logarithmic scale
    log(magI, magI);
    
// 如果频谱的行数或列数为奇数,则对其进行裁剪
    magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
// 重新排列傅里叶像的象限,使原点在像中心
    int cx = magI.cols/2;
    int cy = magI.rows/2;
    Mat q0(magI, Rect(0, 0, cx, cy));   // Top-Left - Create a ROI per quadrant
    Mat q1(magI, Rect(cx, 0, cx, cy));  // Top-Right
    Mat q2(magI, Rect(0, cy, cx, cy));  // Bottom-Left
    Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
    Mat tmp;                           // swap quadrants (Top-Left with Bottom-Right)
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp);                    // swap quadrant (Top-Right with Bottom-Left)
    q2.copyTo(q1);
    tmp.copyTo(q2);
    
    normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
                                            // viewable image form (float between values 0 and 1).
    imshow("Input Image"       , I   );    // Show the result
    imshow("spectrum magnitude", magI);
    waitKey();
    return EXIT_SUCCESS;
}

	一个应用的想法是确定图像中呈现的几何方向。例如,让我们看看一个文本是否水平?看一些文本,你会注意到文本线也有水平线字母也有垂直线。在傅里叶变换的情况下,也可以看到文本片段的这两个主要成分。让我们使用这个水平图像和这个旋转的文本图像。
	您可以看到,频域最有影响力的分量(亮度图像上最亮的点)跟随图像上对象的几何旋转。由此,我们可以计算偏移量,并执行图像旋转来纠正最终错过的对准。
使用XML和YAML进行文件输入和输出

您将看到如何使用OpenCV的cv::FileStorage数据结构将数据读写为XML或YAML文件格式。

你会找到以下问题的答案:
如何打印和读取文本条目到文件和OpenCV使用YAML或XML文件?
如何做同样的OpenCV数据结构?
如何为你的数据结构做到这一点?
使用OpenCV数据结构,如cv::FileStorage, cv::FileNode或cv::FileNodeIterator。
如何使用OpenCV的parallel_for_并行化你的代码

您将看到如何使用OpenCV parallel_for_轻松地并行化代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷咪哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值