opencv_opencv3 - CSDN
opencv 订阅
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。 [1]  它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。OpenCV用C++语言编写,它具有C ++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS,OpenCV主要倾向于实时视觉应用,并在可用时利用MMX和SSE指令, 如今也提供对于C#、Ch、Ruby,GO的支持。 [2] 展开全文
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。 [1]  它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。OpenCV用C++语言编写,它具有C ++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS,OpenCV主要倾向于实时视觉应用,并在可用时利用MMX和SSE指令, 如今也提供对于C#、Ch、Ruby,GO的支持。 [2]
信息
开发商
OpenCV
软件授权
Intel、Willow Garage、NVIDIA等 [3]
软件名称
OpenCV
更新时间
2020-04-06
软件版本
OpenCV–4.3.0 [2]
软件平台
Linux、Windows、Android、Mac OS等
软件语言
C++
软件大小
1.16GB
opencv历史发展
OpenCV 拥有包括 500 多个C函数的跨平台的中、高层 API。它不依赖于其它的外部库——尽管也可以使用某些外部库。OpenCV 为Intel® Integrated Performance Primitives(IPP)提供了透明接口。这意味着如果有为特定处理器优化的 IPP 库,OpenCV 将在运行时自动加载这些库。 [4]  (注:OpenCV 2.0版的代码已显著优化,无需IPP来提升性能,故2.0版不再提供IPP接口) [5]  最新版本是3.4 ,2017年12月23日发布 [6]  。1999年1月,CVL项目启动。主要目标是人机界面,能被UI调用的实时计算机视觉库,为Intel处理器做了特定优化。2000年6月,第一个开源版本OpenCV alpha 3发布。2000年12月,针对linux平台的OpenCV beta 1发布。2006年,支持Mac OS的OpenCV 1.0发布。2009年9月,OpenCV 1.2(beta2.0)发布。2009年10月1日,Version 2.0发布。2010年12月6日,OpenCV 2.2发布。2011年8月,OpenCV 2.3发布。2012年4月2日,发布OpenCV 2.4。2014年8月21日,发布OpenCv 3.0 alpha。2014年11月11日,发布OpenCV 3.0 beta。2015年6月4日,发布OpenCV 3.0。2016年12月,发布OpenCV 3.2版(合并969个修补程序,关闭478个问题)2017年8月3日,发布OpenCV 3.3版(最重要的更新是把DNN模块从contrib里面提到主仓库)OpenCV 使用类BSDlicense,所以对非商业应用和商业应用都是免费(FREE)的。(细节参考 license)OpenCV提供的视觉处理算法非常丰富,并且它部分以C语言编写,加上其开源的特性,处理得当,不需要添加新的外部支持也可以完整的编译链接生成执行程序,所以很多人用它来做算法的移植,OpenCV的代码经过适当改写可以正常的运行在DSP系统和ARM嵌入式系统中,这种移植在大学中经常作为相关专业本科生毕业设计或者研究生课题的选题。
收起全文
  • 基于OpenCV最新版本OpenCV4开始,从基础开始,详解OpenCV核心模块Core、Imgproc、video analysis核心API与相关知识点,讲解从图像组成,像素操作开始,一步一步教你如何写代码,如何用API解决实际问题,从图像处理到...
  • Python+OpenCV计算机视觉

    2018-12-28 17:42:46
    Python+OpenCV计算机视觉系统全面的介绍。
  • OpenCV视频分析与对象跟踪实战视频培训课程概况:基于OpenCV新版本3.2 从基本的OpenCV视频读写与摄像头输入视频API讲解开始, 用原理与代码实现相结合的方式讲述了视频背景建模、前景ROI对象获取、特定颜色对象发现与...
  • OpenCV 概述

    2014-03-09 16:36:39
    OpenCV概述 OpenCV是Intel开元计算机视觉库,它有一系列开源的C函数和少量C++类构成,实现了图像处理和计算机视觉很多通用的算法。 OpenCV特点: 1.拥有包括300多个C函数的跨平台的中、高层API; 2.跨平台:...

    OpenCV概述

    OpenCV是Intel开元计算机视觉库,它有一系列开源的C函数和少量C++类构成,实现了图像处理和计算机视觉很多通用的算法。


    OpenCV特点:

    1.拥有包括300多个C函数的跨平台的中、高层API;

    2.跨平台:Windows/Linux;

    3.Free:无论是对于商业还是非商业应用;

    4.速度快;

    5.使用方便。


    OpenCV结构:


    CXCORE:包含数据结构、矩阵运算、数据变换、对象持久(Object Persistence)、内存管理、错误处理、动态连接、绘图、文本和基本的数学功能。

    CV:包含图形处理、图像结构分析、运动描述和跟踪、模式识别和摄像机标定。

    Machine Learning(ML):包含许多聚类,分类和数据分析函数。

    HighGUI:包含图形用户界面和图像/视频的读/写。

    CVCAM:摄像机接口,在OpenCV1.0以后的版本中被移除。


    OpenCV编码样式指南:

    文件命名:有CV和CVAUS库文件的命名必须服从于以下规则:

    1.所有的CV库文件名前缀为cv;

    2.混合的C/C++接口头文件扩展名为.h;

    3.纯C++接口头文件扩展名为.hpp;

    4.实现头文件扩展名为.cpp;

    5.为了与POSIX兼容,文件名都以小写字符组成。


    命名约定:

    1.OpenCV中使用大小写混合样式来标识外部函数、数据类型和类方法。

    2.宏全部使用大写字符,词间用下划线分隔。

    3.所有的外部或内部名称,若在多个文件中可见,则必须含有前缀:

       外部函数使用前缀cv;

       内部函数使用前缀lcv;

       数据结构(C结构体、枚举、联合体和类)使用前缀CV

       外部或某些内部宏使用前缀CV_

       内部宏使用前缀ICV_


    接口函数设计:

       为了保持库的一致性,以如下方式设计接口非常关键。函数接口元素包括:功能、名称、返回值、参数类型、参数顺序和参数默认值。

       函数功能必须定义良好并保持精简。函数应该容易镶入到使用其它OpenCV函数的不同处理过程。函数名称应该简单并能体现函数的功能。

       大多数函数名形式:cv<ActionName>


    图像处理---平滑处理

      “平滑处理”也称为“模糊处理”,是一项简单且使用频率很高的图像处理方法。平滑处理最常见的用途是用来减少图像上的噪声或者失真。目前OpenCV中,所有平滑处理操作都由cvSmooth函数实现。

    函数说明:

    void cvSmooth(const CvArr* src, CvArr* dst, int smoothtype=CV_GAUSSIAN, int param1=3, int param2=0, double param3=0, double param4=0);

      src和dst分别是平滑操作的输入头像和结果,cvSmooth()函数包含4个参数:param1-param4. 这些参数的含义取决于smoothtype的值。


    图像处理---图像形态学

      OpenCV为进行图像形态学变换提供了快速、方便的函数,基于形态学的转换是膨胀与腐蚀,他们能够实现多种功能:消除噪声,分隔出独立的图像元素以及在图像中连接相邻的元素。

      膨胀是指将一个图像(或图像中的一部分区域,称之为A)与核(称之为B)进行卷积。即,计算核B覆盖区域的像素最大值,并把这个最大值赋值给参考点指定的坐标。这样就会使图像中的高亮区域逐渐增大。

      腐蚀是膨胀的反操作。腐蚀操作要求计算核区像素的最小值。即,当核B与图像卷积时,计算被核B覆盖区域的最小像素值,并把这个值放到参考点。

      在OpenCV,利用cvErode()和cvDilate()函数实现上述变换:

    void cvErode(lplImage* src, lplIMage* dst, lpIlConvKernel* B=NULL, int iterations=1);

    void cvDilate(lplImage* src, lplIMage* dst, lpIlConvKernel* B=NULL, int iterations=1);

    该两个函数的第三个参数是核,默认值是NULL。当为空时,所使用的是参考点位于中心的3*3核。第四个参数是迭代的次数。

    开运算:将二值图像先腐蚀,然后,再膨胀,通常用来统计二值图像中的区域数。开运算则去除一些小的斑点。

    闭运算:将二值图像先膨胀,然后,再腐蚀,通常用来去除噪声引起的区域。闭运算使得小洞被填上,临近的目标连接到了一起(任何结构元容纳不下的小洞或者缝隙都会被填充)。

      对于连通区域分析,通常先采用腐蚀或者闭运算来消除纯粹由噪声引起的部分,然后用开运算来连接临近的区域。最显著的效果是,闭运算消除了低于临近点的孤立点,而,开运算是消除高于其临近点的孤立点。 


    图像处理---拉普拉斯变换

    OpenCV的拉普拉斯函数实现了拉普拉斯算子的离散模拟。

    OpenCV中,拉普拉斯变换函数为:

    void cvLaplace(const CvArr* src, CvArr* dst, int apertureSize=3);

    该函数通常把源图像和目标图像以及中控大小作为变量。源图像既可以是8位(无符号)图像,也可以是32位(浮点)图像。而目标图像必须是16位(有符号)或者32位(浮点)图像。因为拉普拉斯算子的形式是沿着X轴和Y轴的二次导数的和,这就意味着周围是更高值的单电或者小块会将使这个函数值最大化。反过来,周围是更低值的点将会是函数的负值最大化。反过来,周围是更低值的点将会是函数的负值最大化。基于这种思想,拉普拉斯也可以用于边缘检测。


    图像处理---Cannay算子

      Canny边缘检测法与基于拉普拉斯算法的不同点之一是在Canny算法中,首先在x和y方向求一阶导数,然后,组合为4个方向的导数。这些方向导数达到局部最大值的点就是组成边缘的候选点。另外,Canny算法的最终要的新特点是阈值的概念,如果一个像素的梯度大于上线阈值,则被认为是边缘像素,如果低于下限阈值,则被抛弃,如果介于二者时间,只有当其与高于上限阈值的像素连接时才会被接受。Canny推荐的上下限阈值比为2:1到3:1之间。

      void cvCanny(const CvArr* img, CvArr* edges, double lowThresh, double highThresh, int apertureSize=3);

      此函数是需要输入一张灰度图,输出也是灰度的。


      形态学操作

      形态学操作主要有四种:腐蚀、膨胀、开和闭。最基本的形态学操作是腐蚀和膨胀。其他的操作都可以通过腐蚀和膨胀推导出来。

      用集合论的观点介绍他们很繁琐。这里换一种思路,如下:

      我们先做一定的假设:对于一幅图像,前景(我们感兴趣的部分);背景(不感兴趣的部分)是黑色的。然后就可以望文生义一下了,腐蚀操作会使得前景变小,而膨胀会使得前景变大。这主要是当结构元(用来对图像处理的基本模板)作用于图像的边沿时,两种操作的定义引起的。腐蚀操作时,只有当整个结构元都在图像边沿内时,锚点(结构元与图像中每个像素对其的点,通常取作结构元的几何中心)对准的像素才会被保留,判为前景;否则,这个点判为背景。膨胀操作则是只要结构元与图像有交集时,锚点对准的像素就会被保留,判为前景。腐蚀可以用来消除一些小的误检测的前景;而膨胀则可以填充一些小洞。

      注意到,用3*3的模板腐蚀3次与用7*7的模板腐蚀一次效果是相同的。膨胀的结果可以类推。

      

    展开全文
  • Opencv学习(一)

    2018-10-05 16:12:06
    Opencv学习笔记(一) 1、Image Watch 图像监视是微软VisualStudio的插件,它允许您在调试应用程序时在内存映像中可视化。这有助于跟踪错误,或者简单地理解给定代码的操作。 可以在vs里面的工具--&amp;...

    1、Image Watch

    图像监视是微软VisualStudio的插件,它允许您在调试应用程序时在内存映像中可视化。这有助于跟踪错误,或者简单地理解给定代码的操作。

    可以在vs里面的工具-->扩展和更新里面获取Image Watch

    然后再视图-->其他窗口调用。

    2、Load and Display an Image

    Code

    #include <opencv2/core/core.hpp>
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <iostream>
    #include <string>
    using namespace cv;
    using namespace std;
    int main(int argc, char** argv)
    {
    	string imageName("1.jpg"); // by default
    	if (argc > 1)
    	{
    		imageName = argv[1];
    	}
    	Mat image;
    	image = imread(imageName.c_str(), IMREAD_COLOR); // Read the file
    	if (image.empty())                      // Check for invalid input
    	{
    		cout << "Could not open or find the image" << std::endl;
    		return -1;
    	}
    	namedWindow("Display window", WINDOW_AUTOSIZE); // Create a window for display.
    	imshow("Display window", image);                // Show our image inside it.
    	waitKey(0); // Wait for a keystroke in the window
    	return 0;
    }
    

    vs2017中使用命令行参数调试程序

    在这里插入图片描述

    Explanation

    现在我们调用CV::imRead函数,它加载由第一个参数指定的图像名称(ARGV〔1〕)。第二个参数指定了我们想要的图像的格式。

    IimeRead不变(<0)按原样加载图像(包括alpha通道,如果存在)

    IimeRead灰度(0)将图像加载为强度图像。

    IdRead颜色(> 0)以RGB格式加载图像

    image = imread(imageName.c_str(), IMREAD_COLOR); // Read the file
    

    在检查图像数据被正确加载之后,我们想要显示我们的图像,所以我们使用CV::NAMEDLE窗口函数创建OpenCV窗口。一旦创建了OpenCV,它们就会自动管理。为此,您需要指定它的名称以及它应该如何从大小的角度来处理它包含的图像的变化。

    如果不使用QT后端,则只支持WistWOWAutoSead。在这种情况下,窗口大小将占用它所显示的图像的大小。不允许调整大小!

    Qt上的Windows WORKWORD,您可以使用它来允许窗口调整大小。图像将根据当前窗口大小调整自身大小。通过使用该运算符,还需要指定是否希望图像保持其纵横比(WOWDOWKEEPRATIO)或不(WOWDOWFRIERATIO)。

    namedWindow( "Display window", WINDOW_AUTOSIZE ); // Create a window for display.
    

    最后,使用新的图像更新OpenCV窗口的内容,使用imshow函数。指定要更新的OpenCV窗口名称和在此操作期间要使用的图像:

     imshow( "Display window", image ); // Show our image inside it.
    

    因为我们希望我们的窗口显示出来,直到用户按下一个键(否则程序将结束得太快),我们使用CV::WaWiKEY函数,它的唯一参数是它等待用户输入的时间(毫秒测量)。零意味着永远等待。

    waitKey(0); // Wait for a keystroke in the window
    

    3、Load, Modify, and Save an Image

    Code:

    #include <opencv2/opencv.hpp>
    using namespace cv;
    using namespace std;
    int main(int argc, char** argv)
    {
    	char* imageName = argv[1];
    	Mat image;
    	image = imread(imageName, 1);
    	if (argc != 2 || !image.data)
    	{
    		printf(" No image data \n ");
    		return -1;
    	}
    	Mat gray_image;
    	cvtColor(image, gray_image, COLOR_BGR2GRAY);
    	imwrite("Gray_Image.jpg", gray_image);
    	namedWindow(imageName, WINDOW_AUTOSIZE);
    	namedWindow("Gray image", WINDOW_AUTOSIZE);
    	imshow(imageName, image);
    	imshow("Gray image", gray_image);
    	waitKey(0);
    	return 0;
    }
    

    Explanation

    1.imread不在赘述

    2. cv::cvtColor

    cvtColor( image, gray_image, COLOR_BGR2GRAY );

    参数:

    源图像(图像)

    目的地图像(GrayIX图像),在其中我们将保存转换后的图像。

    指示将执行什么类型的转换的附加参数。在这种情况下,我们使用CurryBGR2GRY(因为在彩色图像的情况下具有BGR默认信道顺序)。

    3.imwrite

    imwrite( “…/…/images/Gray_Image.jpg”, gray_image );

    参数:

    写入的图片名字

    写入的Mat

    4、Mat - The Basic Image Container

    MAT基本上是一个具有两个数据部分的类:矩阵标头(包含诸如矩阵的大小、用于存储的方法、地址是存储的矩阵等)和指向包含像素值的矩阵的指针(取决于选择用于存储的方法。矩阵头的大小是恒定的,但是矩阵本身的大小可以从图像到图像变化,并且通常是按数量级大的。

    OpenCV是一个图像处理库。它包含了大量的图像处理功能集合。为了解决计算上的挑战,大多数情况下,您将最终使用库的多个函数。因此,将图像传递给函数是一种常见的做法。我们不应该忘记,我们正在谈论图像处理算法,这往往是相当沉重的计算。我们想做的最后一件事是通过制作不必要的大图像拷贝来进一步降低程序的速度。

    为了解决这个问题,OpenCV使用一个参考计数系统。其思想是每个Mat对象都有自己的标头,但是矩阵可以通过它们的矩阵指针指向相同的地址而在它们的两个实例之间共享。此外,复制运算符只将标题和指针复制到大矩阵,而不是数据本身。

    Mat A, C;                          // creates just the header parts
    A = imread(argv[1], IMREAD_COLOR); // here we'll know the method used (allocate matrix)
    Mat B(A);                                 // Use the copy constructor
    C = A;                                    // Assignment operator
    

    最后,所有这些对象指向相同的单个数据矩阵。然而,它们的标题是不同的,使用它们中的任何一个进行修改也会影响所有其他的标题。在实际应用中,不同的对象只对同一基础数据提供不同的访问方法。然而,它们的标题部分是不同的。真正有趣的部分是,您可以创建仅引用完整数据的一个子段的标题。例如,为了在图像中创建感兴趣区域(ROI),您只需创建具有新边界的新标题:

    Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
    Mat E = A(Range::all(), Range(1,3)); // using row and column boundaries
    

    现在你可能会问,矩阵本身是否可能属于多个物体,当它不再需要时,它负责清理它。简短的回答是:最后一个使用它的对象。这是通过使用参考计数机制来处理的。每当有人复制垫对象的头时,矩阵的计数器就会增加。每当清空报头时,计数器就会减少。当计数器达到零时,矩阵也被释放。有时,您也希望复制矩阵本身,因此OpenCV提供CV::MAT::clone()CV::MAT::CopyTo()函数。

    Mat F = A.clone();
    Mat G;
    A.copyTo(G);
    

    现在修改F或G不会影响垫头所指向的矩阵。你需要记住的是:

    OpenCV函数的输出图像分配是自动的(除非另有说明)。

    你不需要考虑内存管理与opencvs C++接口。

    赋值运算符和复制构造函数只复制报头。

    可以使用CV::Mat:clone()CV::MAT::CopyTo()函数复制图像的底层矩阵。

    构造函数

    CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]

    CVY8UC3意味着我们使用8位长的无符号字符类型,每个像素都有三个来形成三个通道。这是预定义的多达四个频道号码。CV::标量是四元短向量。指定这个值,并且可以用自定义值初始化所有矩阵点。如果需要更多,可以用上宏创建类型,在括号中设置通道编号,如下所示。

    5.How to scan images, lookup tables and time measurement with OpenCV

    The iterator (safe) method

    迭代器方法被认为是一种更安全的方法,因为它从用户那里接管这些任务。所有你需要做的是询问图像矩阵的开始和结束,然后只需增加开始迭代器,直到达到结束。若要获取迭代器所指向的值,请使用*运算符(在它之前加上它)。

    Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
    {
        // accept only char type matrices
        CV_Assert(I.depth() != sizeof(uchar));
        const int channels = I.channels();
        switch(channels)
        {
        case 1:
            {
                MatIterator_<uchar> it, end;
                for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
                    *it = table[*it];
                break;
            }
        case 3:
            {
                MatIterator_<Vec3b> it, end;
                for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
                {
                    (*it)[0] = table[(*it)[0]];
                    (*it)[1] = table[(*it)[1]];
                    (*it)[2] = table[(*it)[2]];
                }
            }
        }
        return I;
    }
    

    在彩色图像的情况下,每列有三个UCHAR项。这可能被认为是一个短向量uChar项目。要访问第n个子列,我们使用简单的运算符[]访问。记住OpenCV迭代器经过列并自动跳转到下一行是很重要的。因此,在彩色图像的情况下,如果使用简单的UCHAR迭代器,则只能访问蓝色通道值。

    Mask operations on matrices

    矩阵上的掩码运算是相当简单的。想法是根据掩模矩阵(也称为内核)重新计算图像中的每个像素值。该掩模保持将调整相邻像素(和当前像素)对新像素值有多大影响的值。从数学的角度,我们用我们的指定值做加权平均。

    void Sharpen(const Mat& myImage, Mat& Result)
    {
        CV_Assert(myImage.depth() == CV_8U);  // accept only uchar images
        Result.create(myImage.size(), myImage.type());
        const int nChannels = myImage.channels();
        for(int j = 1; j < myImage.rows - 1; ++j)
        {
            const uchar* previous = myImage.ptr<uchar>(j - 1);
            const uchar* current  = myImage.ptr<uchar>(j    );
            const uchar* next     = myImage.ptr<uchar>(j + 1);
            uchar* output = Result.ptr<uchar>(j);
            for(int i = nChannels; i < nChannels * (myImage.cols - 1); ++i)
            {
                *output++ = saturate_cast<uchar>(5 * current[i]
                             -current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
            }
        }
        Result.row(0).setTo(Scalar(0));
        Result.row(Result.rows - 1).setTo(Scalar(0));
        Result.col(0).setTo(Scalar(0));
        Result.col(Result.cols - 1).setTo(Scalar(0));
    }
    

    6、Adding (blending) two images using OpenCV

    合成两张图片

    Code:

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    int main(int argc, char** argv)
    {
    	double alpha = 0.5; double beta; double input;
    	Mat src1, src2, dst;
    	std::cout << " Simple Linear Blender " << std::endl;
    	std::cout << "-----------------------" << std::endl;
    	std::cout << "* Enter alpha [0-1]: ";
    	std::cin >> input;
    	if (input >= 0.0 && input <= 1.0)
    	{
    		alpha = input;
    	}
    	src1 = imread("1.jpg");
    	src2 = imread("2.jpg");
    	if (!src1.data) { printf("Error loading src1 \n"); return -1; }
    	if (!src2.data) { printf("Error loading src2 \n"); return -1; }
    	namedWindow("Linear Blend", 1);
    	beta = (1.0 - alpha);
    	addWeighted(src1, alpha, src2, beta, 0.0, dst);
    	imshow("Linear Blend", dst);
    	imwrite("3.jpg", dst);
    	waitKey(0);
    	return 0;
    }
    

    Explanation

    addWeighted( src1, alpha, src2, beta, 0.0, dst);

    图片:src1,src2

    比例:alpha,beta,和为1

    新图片:dst

    7、Changing the contrast and brightness of an image! :

    一般的图像处理算子是一个获取一个或多个输入图像并产生输出图像的函数。

    图像变换可以看作:

    点算子(像素变换)

    邻域(基于区域)算子

    像素变换

    在这种图像处理变换中,每个输出像素的值仅取决于相应的输入像素值。

    这样的例子包括亮度和对比度调整以及颜色校正和变换。

    亮度和对比度调整

    两个常用的点过程是乘法和加法:

    g(x)=αf(x)+β

    参数α>0和β通常称为增益和偏置参数,有时这些参数分别用来控制对比度和亮度。

    可以将f(x)作为源图像像素和g(x)作为输出图像像素。然后,我们更方便地将表达式写成:

    g(i,j)=αf(i,j)+β

    Code

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    double alpha; /*< Simple contrast control */
    int beta;  /*< Simple brightness control */
    int main(int argc, char** argv)
    {
    	Mat image = imread(argv[1]);
    	Mat new_image = Mat::zeros(image.size(), image.type());
    	std::cout << " Basic Linear Transforms " << std::endl;
    	std::cout << "-------------------------" << std::endl;
    	std::cout << "* Enter the alpha value [1.0-3.0]: "; std::cin >> alpha;
    	std::cout << "* Enter the beta value [0-100]: "; std::cin >> beta;
    	for (int y = 0; y < image.rows; y++) {
    		for (int x = 0; x < image.cols; x++) {
    			for (int c = 0; c < 3; c++) {
    				new_image.at<Vec3b>(y, x)[c] =
    					saturate_cast<uchar>(alpha*(image.at<Vec3b>(y, x)[c]) + beta);
    			}
    		}
    	}
    	namedWindow("Original Image", 1);
    	namedWindow("New Image", 1);
    	imshow("Original Image", image);
    	imshow("New Image", new_image);
    	imwrite("2.jpg", new_image);
    	waitKey();
    	return 0;
    }
    

    Explanation

    1.我们首先创建参数来保存用户输入的α和β:

    double alpha;
    int beta;
    

    2.我们使用cv::imread加载图像并将其保存在一个MAT对象中:

    Mat image = imread( argv[1] );
    

    3.我们使用CV::iMead加载图像并将其保存在一个Mat对象中:现在,因为我们将对该图像进行一些转换,所以我们需要一个新的Mat对象来存储它。此外,我们希望这具有以下特征:

    初始像素值等于零

    与原始图像相同大小和类型

    Mat new_image = Mat::zeros( image.size(), image.type() );
    

    4.现在,为了执行操作G(i,j)=αf(i,j)+β,我们将访问图像中的每个像素。由于我们正在使用BGR图像,我们将有三个像素(B,G和R)的值,所以我们也将分别访问它们。

    for( int y = 0; y < image.rows; y++ ) {
        for( int x = 0; x < image.cols; x++ ) {
            for( int c = 0; c < 3; c++ ) {
                new_image.at<Vec3b>(y,x)[c] =
                  saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
            }
        }
    }
    

    注意以下事项:

    为了访问图像中的每个像素,我们使用这个语法:图像。在<Vec3b>(Y,X)[C],其中Y是行,X是列,C是R,G或B(0, 1或2)。

    因为运算 αp(i,j)+β可以给出超出范围的值,或者不是整数(如果α是浮点数),我们使用cv::SudialType强制转换来确保值是有效的。

    5.最后,我们创建窗口和显示图像,通常的方式。

    	namedWindow("Original Image", 1);
    	namedWindow("New Image", 1);
    	imshow("Original Image", image);
    	imshow("New Image", new_image);
    	imwrite("2.jpg", new_image);
    	waitKey();
    

    8、Basic Drawing

    Point

    它代表一个2D点,由它的图像坐标x和y指定。我们可以将它定义为:

    Point pt;
    pt.x = 10;
    pt.y = 8;
    //Point pt = Point(10, 8);
    

    Scalar

    表示一个4元向量。在OpenCV中,类型标量被广泛用于传递像素值;

    在本教程中,我们将广泛使用它来表示BGR颜色值(3个参数)。如果不使用最后一个参数,则不必定义最后一个参数。

    让我们来看看一个例子,如果我们要求一个颜色参数,我们给出:

    Scalar( a, b, c )
    

    我们将定义一个BGR颜色,例如:蓝色= A,绿色= B和红色= C。

    Code

    #include <opencv2/core.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/highgui.hpp>
    
    #define w 400
    
    using namespace cv;
    
    /// Function headers
    void MyEllipse( Mat img, double angle );
    void MyFilledCircle( Mat img, Point center );
    void MyPolygon( Mat img );
    void MyLine( Mat img, Point start, Point end );
    
    /**
     * @function main
     * @brief Main function
     */
    int main( void ){
    
      //![create_images]
      /// Windows names
      char atom_window[] = "Drawing 1: Atom";
      char rook_window[] = "Drawing 2: Rook";
    
      /// Create black empty images
      Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
      Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
      //![create_images]
    
      /// 1. Draw a simple atom:
      /// -----------------------
    
      //![draw_atom]
      /// 1.a. Creating ellipses
      MyEllipse( atom_image, 90 );
      MyEllipse( atom_image, 0 );
      MyEllipse( atom_image, 45 );
      MyEllipse( atom_image, -45 );
    
      /// 1.b. Creating circles
      MyFilledCircle( atom_image, Point( w/2, w/2) );
      //![draw_atom]
    
      /// 2. Draw a rook
      /// ------------------
    
      //![draw_rook]
      /// 2.a. Create a convex polygon
      MyPolygon( rook_image );
    
      //![rectangle]
      /// 2.b. Creating rectangles
      rectangle( rook_image,
             Point( 0, 7*w/8 ),
             Point( w, w),
             Scalar( 0, 255, 255 ),
             FILLED,
             LINE_8 );
      //![rectangle]
    
      /// 2.c. Create a few lines
      MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
      MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, w ) );
      MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
      MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );
      //![draw_rook]
    
      /// 3. Display your stuff!
      imshow( atom_window, atom_image );
      moveWindow( atom_window, 0, 200 );
      imshow( rook_window, rook_image );
      moveWindow( rook_window, w, 200 );
    
      waitKey( 0 );
      return(0);
    }
    
    /// Function Declaration
    
    /**
     * @function MyEllipse
     * @brief Draw a fixed-size ellipse with different angles
     */
    //![my_ellipse]
    void MyEllipse( Mat img, double angle )
    {
      int thickness = 2;
      int lineType = 8;
    
      ellipse( img,
           Point( w/2, w/2 ),
           Size( w/4, w/16 ),
           angle,
           0,
           360,
           Scalar( 255, 0, 0 ),
           thickness,
           lineType );
    }
    //![my_ellipse]
    
    /**
     * @function MyFilledCircle
     * @brief Draw a fixed-size filled circle
     */
    //![my_filled_circle]
    void MyFilledCircle( Mat img, Point center )
    {
      circle( img,
          center,
          w/32,
          Scalar( 0, 0, 255 ),
          FILLED,
          LINE_8 );
    }
    //![my_filled_circle]
    
    /**
     * @function MyPolygon
     * @brief Draw a simple concave polygon (rook)
     */
    //![my_polygon]
    void MyPolygon( Mat img )
    {
      int lineType = LINE_8;
    
      /** Create some points */
      Point rook_points[1][20];
      rook_points[0][0]  = Point(    w/4,   7*w/8 );
      rook_points[0][1]  = Point(  3*w/4,   7*w/8 );
      rook_points[0][2]  = Point(  3*w/4,  13*w/16 );
      rook_points[0][3]  = Point( 11*w/16, 13*w/16 );
      rook_points[0][4]  = Point( 19*w/32,  3*w/8 );
      rook_points[0][5]  = Point(  3*w/4,   3*w/8 );
      rook_points[0][6]  = Point(  3*w/4,     w/8 );
      rook_points[0][7]  = Point( 26*w/40,    w/8 );
      rook_points[0][8]  = Point( 26*w/40,    w/4 );
      rook_points[0][9]  = Point( 22*w/40,    w/4 );
      rook_points[0][10] = Point( 22*w/40,    w/8 );
      rook_points[0][11] = Point( 18*w/40,    w/8 );
      rook_points[0][12] = Point( 18*w/40,    w/4 );
      rook_points[0][13] = Point( 14*w/40,    w/4 );
      rook_points[0][14] = Point( 14*w/40,    w/8 );
      rook_points[0][15] = Point(    w/4,     w/8 );
      rook_points[0][16] = Point(    w/4,   3*w/8 );
      rook_points[0][17] = Point( 13*w/32,  3*w/8 );
      rook_points[0][18] = Point(  5*w/16, 13*w/16 );
      rook_points[0][19] = Point(    w/4,  13*w/16 );
    
      const Point* ppt[1] = { rook_points[0] };
      int npt[] = { 20 };
    
      fillPoly( img,
            ppt,
            npt,
            1,
            Scalar( 255, 255, 255 ),
            lineType );
    }
    //![my_polygon]
    
    /**
     * @function MyLine
     * @brief Draw a simple line
     */
    //![my_line]
    void MyLine( Mat img, Point start, Point end )
    {
      int thickness = 2;
      int lineType = LINE_8;
    
      line( img,
        start,
        end,
        Scalar( 0, 0, 0 ),
        thickness,
        lineType );
    }
    

    Explanation

    1.由于我们计划画两个例子(原子和一个木棒),我们必须创建两个图像和两个窗口来显示它们。

    char atom_window[] = "Drawing 1: Atom";
    char rook_window[] = "Drawing 2: Rook";
    Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
    Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
    

    2.我们创建了绘制不同几何形状的函数。例如,为了画原子,我们使用了MyEngEs和MyFilledCircle:

    MyEllipse( atom_image, 90 );
    MyEllipse( atom_image, 0 );
    MyEllipse( atom_image, 45 );
    MyEllipse( atom_image, -45 );
    MyFilledCircle( atom_image, Point( w/2.0, w/2.0) );
    

    3.为了绘制木屋,我们采用了MyLoinrectangleMyPosion

    
    MyPolygon( rook_image );
    rectangle( rook_image,
           Point( 0, 7*w/8.0 ),
           Point( w, w),
           Scalar( 0, 255, 255 ),
           -1,
           8 );
    MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
    MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, w ) );
    MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
    MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );
    

    4.让我们检查一下这些函数的内部内容:

    MyLine

    void MyLine( Mat img, Point start, Point end )
    {
        int thickness = 2;
        int lineType = 8;
        line( img, start, end,
              Scalar( 0, 0, 0 ),
              thickness,
              lineType );
    }
    

    正如我们所看到的,MyLayle只调用函数cv::Load,它执行以下操作:

    从点到点画一条线

    该线显示在图像IMG中。

    线条颜色由标量(0, 0, 0)定义,这是布莱克的RGB值。

    线的厚度设置为厚度(在这种情况下为2)。

    该线是8连通的(LyeType=8)。

    MyEllipse

    void MyEllipse( Mat img, double angle )
    {
        int thickness = 2;
        int lineType = 8;
        ellipse( img,
           Point( w/2.0, w/2.0 ),
           Size( w/4.0, w/16.0 ),
           angle,
           0,
           360,
           Scalar( 255, 0, 0 ),
           thickness,
           lineType );
    }
    

    从上面的代码中,我们可以看到函数cv::fillPoly绘制椭圆,使得:

    椭圆在图像IMG中显示。

    椭圆中心位于点**(w/2,w/2),并被封装在大小为的框中(w/4,w/16)**。

    椭圆旋转角度

    椭圆在0度和360度之间延伸一个弧。

    图形的颜色将是标量(255, 0, 0),这意味着蓝色在RGB值。

    椭圆的厚度为2。

    MyFilledCircle

    void MyFilledCircle( Mat img, Point center )
    {
        int thickness = -1;
        int lineType = 8;
        circle( img,
            center,
            w/32.0,
            Scalar( 0, 0, 255 ),
            thickness,
            lineType );
    }
    

    类似于椭圆函数,我们可以观察到圆圈作为参数接收:

    将显示圆的图像(IMG)

    以圆心为中心的圆的中心

    圆的半径:W/32

    圆的颜色:标量(0, 0, 255),表示BGR中的红色。

    由于厚度=1,将画圆填充。

    MyPolygon

    void MyPolygon( Mat img )
    {
        int lineType = 8;
        /* Create some points */
        Point rook_points[1][20];
        rook_points[0][0] = Point( w/4.0, 7*w/8.0 );
        rook_points[0][1] = Point( 3*w/4.0, 7*w/8.0 );
        rook_points[0][2] = Point( 3*w/4.0, 13*w/16.0 );
        rook_points[0][3] = Point( 11*w/16.0, 13*w/16.0 );
        rook_points[0][4] = Point( 19*w/32.0, 3*w/8.0 );
        rook_points[0][5] = Point( 3*w/4.0, 3*w/8.0 );
        rook_points[0][6] = Point( 3*w/4.0, w/8.0 );
        rook_points[0][7] = Point( 26*w/40.0, w/8.0 );
        rook_points[0][8] = Point( 26*w/40.0, w/4.0 );
        rook_points[0][9] = Point( 22*w/40.0, w/4.0 );
        rook_points[0][10] = Point( 22*w/40.0, w/8.0 );
        rook_points[0][11] = Point( 18*w/40.0, w/8.0 );
        rook_points[0][12] = Point( 18*w/40.0, w/4.0 );
        rook_points[0][13] = Point( 14*w/40.0, w/4.0 );
        rook_points[0][14] = Point( 14*w/40.0, w/8.0 );
        rook_points[0][15] = Point( w/4.0, w/8.0 );
        rook_points[0][16] = Point( w/4.0, 3*w/8.0 );
        rook_points[0][17] = Point( 13*w/32.0, 3*w/8.0 );
        rook_points[0][18] = Point( 5*w/16.0, 13*w/16.0 );
        rook_points[0][19] = Point( w/4.0, 13*w/16.0) ;
        const Point* ppt[1] = { rook_points[0] };
        int npt[] = { 20 };
        fillPoly( img,
                  ppt,
                  npt,
                      1,
                  Scalar( 255, 255, 255 ),
                  lineType );
    }
    

    为了画一个填充多边形,我们使用函数cv:fIyPull。我们注意到:

    多边形将绘制在IMG上。

    多边形的顶点是PPT中的点集。

    要绘制的顶点的总数是NPT

    绘制的多边形数仅为1个。

    多边形的颜色由标量(255, 255, 255)定义,这是白色的BGR值。

    rectangle

    rectangle( rook_image,
               Point( 0, 7*w/8.0 ),
               Point( w, w),
               Scalar( 0, 255, 255 ),
               -1, 8 );
    

    最后,我们有了cv::rectangle函数(我们没有为这个家伙创建一个特殊的函数)。我们注意到:

    矩形将绘制在Rookx图像上。

    矩形的两个相对顶点由**点(0, 7×w/8)**和点(w,w)**定义。

    矩形的颜色由标量(0, 255, 255)给出,这是黄色的BGR值。

    由于厚度值由** -1 **给出,矩形将被填充。

    9、Put Text, Point or Circle

        //插入文字  
        //参数为:承载的图片,插入的文字,文字的位置(文本框左下角),字体,大小,颜色  
        string words= "good luck";  
        putText( picture, words, Point( picture.rows/2,picture.cols/4),CV_FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 0) );  
        imshow("1.jpg",picture);  
    
    //参数为:承载的图像、圆心、半径、颜色、粗细、线型、最后一个0  
    circle(picture,center,r,Scalar(0,0,0));  
    

    椭圆

       //参数为:承载的图像、圆心、长短轴、径向夹角(水平面到长轴的夹角)、起始角度(长轴到起始边沿的夹角)、结束角度(长轴到结束点的夹角)、倾斜的矩形(可选项)、颜色、粗细、线性、偏移  
        ellipse(picture,center,Size( 250, 100 ),0,30,240,Scalar(0,0,0));  
    

    line:

        //画线  
        Point a = Point (600,600);  
        //参数为:承载的图像、起始点、结束点、颜色、粗细、线型  
        line(picture,a,center,Scalar(255,0,0));  
        imshow("1.jpg",picture);  
    

    矩形:

    //画矩形  
    //参数为:承载的图像、顶点、对角点、颜色(这里是蓝色)、粗细、大小  
    rectangle(picture,a,center,Scalar(255,0,0));  
    imshow("底板",picture); 
    

    10、Discrete Fourier Transform

    傅立叶变换将图像分解成其正弦和余弦分量。换句话说,它将将图像从其空间域转换到其频域。其思想是任何函数都可以用无穷窦和余弦函数之和精确地近似。

    变换的结果是复数。通过真实图像和复数图像或通过幅度和相位图像来显示这一点是可能的。然而,在整个图像处理算法中,只有幅度图像是有趣的,因为这包含了关于图像几何结构所需的所有信息。然而,如果你打算对这些表单中的图像做一些修改,然后你需要重新变换它,你就需要保存这两个。

    在这个示例中,我将展示如何计算和显示傅立叶变换的幅度图像。在数字图像的情况下是离散的。这意味着它们可以从给定的域值中获取一个值。例如,在基本灰度值中,图像值通常介于0和255之间。因此,傅立叶变换也需要是离散的类型,导致离散傅立叶变换(DFT)。每当你需要从几何的角度来确定图像的结构时,你就需要使用它。这里是要遵循的步骤(在灰度输入图像I的情况下):

    1.Expand the image to an optimal size将图像扩展到最佳大小。

    DFT的性能取决于图像的大小。它往往是最快的图像大小是倍数的二,三和五。因此,为了达到最大的性能,通常将一个边界值映射到图像以获得具有这些特征的大小是一个好主意。CV::GoDestaldDftsie()返回这个最佳大小,我们可以使用CV::CopyMaBurdReand()函数来扩展图像的边框:

    Mat padded;                            //expand input image to optimal size
    int m = getOptimalDFTSize( I.rows );
    int n = getOptimalDFTSize( I.cols ); // on the border add zero pixels
    copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
    

    2.Make place for both the complex and the real values为复杂的和真实的价值创造位置。

    傅立叶变换的结果是复杂的。这意味着对于每个图像值,结果是两个图像值(每个分量一个)。此外,频域范围远大于其空间对应部分。因此,我们通常至少以浮点格式存储这些文件。因此,我们将将输入图像转换为这种类型,并将其扩展到另一个通道以保存复数值:

    Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
    Mat complexI;
    merge(planes, 2, complexI);         // Add to the expanded another plane with zeros
    

    3.Make the Discrete Fourier Transform进行离散傅立叶变换。

    有可能进行就地计算(与输出相同的输入):

    
    

    4.Transform the real and complex values to magnitude将实数和复值变换为幅值。

    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];
    

    5.Switch to a logarithmic scale切换到对数刻度。

    结果表明,傅立叶系数的动态范围太大,不能在屏幕上显示。我们有一些小的和一些高度变化的值,我们不能像这样观察到。因此,高值将全部变为白色点,而小值则变为黑色。为了使用灰度值来可视化,我们可以将我们的线性尺度转换成对数尺度

    翻译成OpenCV代码:

    magI += Scalar::all(1);                    // switch to logarithmic scale
    log(magI, magI);
    

    6.Crop and rearrange

    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);
    
    

    请记住,在第一步,我们扩大了图像?是时候抛弃新引进的价值观了。为了可视化目的,我们也可以重新排列结果的象限,以便原点(0,0)对应于图像中心。

    7.Normalize.归一化。

    这是为了可视化目的再次进行的。我们现在有了大小,但是这仍然是我们的图像显示范围为0至1。我们使用CV::标准化()函数将我们的值标准化到这个范围

    normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
                                              // viewable image form (float between values 0 and 1).
    

    11、文件的读入与写入XML

        cv::FileStorage(const string& source, int flags, const string& encoding=string());  
    

    参数:

    **source –**存储或读取数据的文件名(字符串),其扩展名(.xml 或 .yml/.yaml)决定文件格式。

    flags – 操作模式,包括:

    • FileStorage::READ 打开文件进行读操作
    • FileStorage::WRITE 打开文件进行写操作
    • FileStorage::APPEND打开文件进行附加操作
    • FileStorage::MEMORY 从source读数据,或向内部缓存写入数据(由FileStorage::release返回)

    encoding – 文件编码方式。目前不支持UTF-16 XML 编码,应使用 8-bit 编码

    演示写入数值、矩阵、多个变量、当前时间和关闭文件:

        // 1.create our writter  
            cv::FileStorage fs("test.yml", FileStorage::WRITE);  
              
            // 2.Save an int  
            int imageWidth= 5;  
            int imageHeight= 10;  
            fs << "imageWidth" << imageWidth;  
            fs << "imageHeight" << imageHeight;  
          
            // 3.Write a Mat  
            cv::Mat m1= Mat::eye(3,3, CV_8U);  
            cv::Mat m2= Mat::ones(3,3, CV_8U);  
            cv::Mat resultMat= (m1+1).mul(m1+2);  
            fs << "resultMat" << resultMat;  
          
            // 4.Write multi-variables   
            cv::Mat cameraMatrix = (Mat_<double>(3,3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);  
            cv::Mat distCoeffs = (Mat_<double>(5,1) << 0.1, 0.01, -0.001, 0, 0);  
            fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;  
          
            // 5.Save local time  
            time_t rawtime; time(&rawtime); //#include <time.h>  
            fs << "calibrationDate" << asctime(localtime(&rawtime));  
          
            // 6.close the file opened  
            fs.release();  
    
    展开全文
  • 在使用MFC导入opencv3图片时,博主查找了许多博客,但是有的步骤实现起来麻烦,有的实现效果差,还有一些就是比较落后适用与opencv2.X版本。接下来我把自己查阅资料实现在VS2017MFC上添加打开opencv3.X图片的详细...

    在使用MFC导入opencv3图片时,博主查找了许多博客,但是有的步骤实现起来麻烦,有的实现效果差,还有一些就是比较落后适用于opencv2.X版本。接下来我把自己查阅资料实现在VS2017MFC上添加打开opencv3.X图片的详细实现方法分享给大家,手把手带你实现MFC打开opencv图片。

    1. 在MFC中配置好opencv3的环境
    这步相信大家都已经配置好了,网上详细教程特别多,在此就不详细展开。简要来说:(打开MFC工程,添加opencv3.X的包含目录、库目录、添加依赖。。。)

    2. 新建 MFC 对话框项目
    基于对话框,默认设置或者在静态库中使用MFC都可,完成。

    3. 添加和设置控件
    (1)工具箱,打开工具箱 ⇒ 添加 Button 控件;
    右击添加的按钮,属性,将其 Caption 改为 ”打开图片“;
    (2)同理,添加Picture Control控件

    打开Picture 控件属性窗口,其 ID 为 IDC_STATIC,修改Type 为 Rectangle,将 Picture 控件大小调整合适(用于最终图像的显示)
    (3)右击按钮,添加事件处理程序

    4. 添加 OpenCV 的 CvvImage 文件(.h, .cpp)
    这些是我从github下载下来的,在opencv3.4.1亲测可用,下面是下载好的上传到我的资源了(没多大就是一个.cpp文件,一个.h文件),当然你也可以从github下载。
    CvvImage.h 下载地址:https://download.csdn.net/download/weixin_42512266/11040964
    CvvImage.cpp 下载地址:https://download.csdn.net/download/weixin_42512266/11040949
    下载好,移动到自己所建工程的文件夹下,不然会无法打开源文件。

    在VS中解决方案资源管理器中,
    【头文件】(右键)⇒ 添加现有项 ⇒ CvvImage.h
    添加头文件
    【源文件】(右键)⇒ 添加现有项 ⇒ CvvImage.cpp
    添加源文件
    添加完成

    5. 添加代码
    1)打开 C**Dlg 类对应的头文件(**Dlg.h)的起始位置处添加:

    #include <opencv\cv.h>
    
    #include <opencv\highgui.h>
    
    #include "CvvImage.h"
    

    头文件
    2)在(*Dlg.h)文件最后的 C**Dlg 类的声明中添加 DrawPicToHDC()成员函数的声明:

    void DrawPicToHDC(IplImage *img, UINT ID);
    

    添加类
    3)C*Dlg 类对应的源文件(*Dlg.cpp)中添加 DrawPicToHDC() 函数的实现(在末尾处添加即可):记得修改下面代码中C**Dlg 类的名称

    void CMFCV1Dlg::DrawPicToHDC(IplImage *img, UINT ID)
    {
        CDC *pDC = GetDlgItem(ID)->GetDC();
        HDC hDC= pDC->GetSafeHdc();
        CRect rect;
        GetDlgItem(ID)->GetClientRect(&rect);
        CvvImage cimg;
        cimg.CopyOf( img ); // 复制图片
        cimg.DrawToHDC( hDC, &rect ); // 将图片绘制到显示控件的指定区域内
        ReleaseDC( pDC );
    }
    

    添加函数
    4)为新增按钮的响应函数(OnBnClickedOpenImg)添加代码:

    void CMFCV1Dlg::OnBnClickedButton1()
    {
        // TODO: 在此添加控件通知处理程序代码
        IplImage *image=NULL; //原始图像
        if(image) cvReleaseImage(&image);
        image = cvLoadImage("D:\\图片\\01.jpg",1); //显示图片
        DrawPicToHDC(image, IDC_STATIC);
    }
    

    6. 编译运行,大功告成
    结果1
    结果2

    7. 打开多张图片
    只需再添加一个Button 控件按钮、一个Picture Control控件就行了
    在新增的按钮里面增加响应函数,与单张的代码完全一样,只需修改最后一句DrawPicToHDC(image, IDC_STATIC);里面的IDC_STATIC,为新增的图片控件ID即可。

    修改
    最终结果为:
    多张

    展开全文
  • 转载:https://blog.csdn.net/qq_22636145/article/details/70185561最近微软发布了VS2017,增加了很多更实用的功能,其中让笔者兴奋不已的GPU加速功能。...其中包括笔者在标题上提到的Opencv3.2以及笔...

    转载:https://blog.csdn.net/qq_22636145/article/details/70185561

    最近微软发布了VS2017,增加了很多更实用的功能,其中让笔者兴奋不已的GPU加速功能。只需要切换一个按键便能享受GPU的加速,让程序的速度几何倍数的加快。

    可是vs2017比较刚刚发布很多的第三方库和第三方的工具都没有更新过来。其中包括笔者在标题上提到的Opencv3.2以及笔者以前一直使用的C++第三方GUI库QT。可是笔者着实不想放弃这令人着迷的VS2017。于是千方百计,到处翻资料终于在MFC单文档下显示出了opencv的图片。

    一,首先要配置好opencv

    因为opencv官网中编译的.dll文件并不支持VS2017。所以我们自己要编译opencv的dll文件。这方面网络上有很多资料,主要的过程是在CMake的软件下用vs2017的编译工具编译opencv的源文件再生成其特定的dll文件便可以直接在VS2017下直接使用opencv了。

    二,在MFC单文档下先显示图片

    VC++的单文档可以直接显示VC++库里面的CImgae类,首先我们在***View.h中添加一个变量CImage image;这里笔者在private下创建的该变量。然后我们在资源文件中添加一个事件处理将它创建在**View.cpp文件中,函数名任意取。

    1. void CMFCApplication1View::OnOpenImage()  
    2. {  
    3.     // TODO: 在此添加命令处理程序代码  
    4.     CFileDialog dlg(TRUE/*, _T(".bmp"), _T("*.bmp"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("位图文件(*.bmp)|*.bmp | JEPG文件(*.jpg) | *.jpg || ")*/);  
    5.     if (dlg.DoModal() == IDOK)  
    6.     {  
    7.         if (!image.IsNull())  
    8.         {  
    9.             image.Destroy();  
    10.         }  
    11.         image.Load(dlg.GetPathName());  
    12.         //CString cstr = dlg.GetPathName();  
    13.         str = CStringA(dlg.GetPathName());  
    14.         pic = imread(str, CV_LOAD_IMAGE_COLOR);  
    15.         Invalidate();  
    16.     }  
    17. }  

    上面便是该函数的全部实现,然后修改OnDraw()函数,如下:

    1. void CMFCApplication1View::OnDraw(CDC* pDC)  
    2. {  
    3.     CMFCApplication1Doc* pDoc = GetDocument();  
    4.     ASSERT_VALID(pDoc);  
    5.     if (!pDoc)  
    6.         return;  
    7.   
    8.     // TODO: 在此处为本机数据添加绘制代码  
    9.     if (!image.IsNull())  
    10.     {  
    11.         image.Draw(pDC->GetSafeHdc(), 0, 0);  
    12.     }  
    13. }  


    修改完成后便可以运行程序点击按钮便可以显示出图片。

    三,实现Mat和CImgae之间的转换。

    1. #include"CImageMat.h"  
    2.   
    3. void Mat2CImage(Mat & mat, CImage & cimage)  
    4. {  
    5.     if (0 == mat.total())  
    6.     {  
    7.         return;  
    8.     }  
    9.   
    10.     int nChannels = mat.channels();  
    11.     if ((1 != nChannels) && (3 != nChannels))  
    12.     {  
    13.         return;  
    14.     }  
    15.     int nWidth = mat.cols;  
    16.     int nHeight = mat.rows;  
    17.   
    18.     cimage.Destroy();  
    19.     cimage.Create(nWidth,nHeight,8*nChannels);  
    20.   
    21.     uchar *pucRow;  
    22.     uchar *pucImage = (uchar*)cimage.GetBits();  
    23.   
    24.     int nStep = cimage.GetPitch();  
    25.   
    26.     if (1 == nChannels)  
    27.     {  
    28.         RGBQUAD* rgbquadColorTable;  
    29.         int nMaxColors = 256;  
    30.         rgbquadColorTable = new RGBQUAD[nMaxColors];  
    31.         cimage.GetColorTable(0, nMaxColors, rgbquadColorTable);  
    32.         for (int nColor = 0; nColor < nMaxColors; nColor++)  
    33.         {   
    34.             rgbquadColorTable[nColor].rgbBlue = (uchar)nColor;  
    35.             rgbquadColorTable[nColor].rgbGreen = (uchar)nColor;       
    36.             rgbquadColorTable[nColor].rgbRed = (uchar)nColor;  
    37.         }  
    38.         cimage.SetColorTable(0, nMaxColors, rgbquadColorTable);  
    39.         delete[]rgbquadColorTable;  
    40.     }  
    41.     for (int nRow = 0; nRow < nHeight; nRow++)  
    42.     {   
    43.         pucRow = (mat.ptr<uchar>(nRow));   
    44.         for (int nCol = 0; nCol < nWidth; nCol++)  
    45.         {   
    46.             if (1 == nChannels)  
    47.             {   
    48.                 *(pucImage + nRow * nStep + nCol) = pucRow[nCol];  
    49.             } else if (3 == nChannels)   
    50.             {   
    51.                 for (int nCha = 0; nCha < 3; nCha++)   
    52.                 {   
    53.                     *(pucImage + nRow * nStep + nCol * 3 + nCha) = pucRow[nCol * 3 + nCha];   
    54.                 }   
    55.             }   
    56.         }   
    57.     }  
    58. }  
    59.   
    60. void CImage2Mat(CImage & cimage, Mat & mat)  
    61. {  
    62.     if (true == cimage.IsNull())  
    63.     {   
    64.         return;   
    65.     }     
    66.     int nChannels = cimage.GetBPP() / 8;   
    67.     if ((1 != nChannels) && (3 != nChannels))   
    68.     {   
    69.         return;   
    70.     }     
    71.     int nWidth = cimage.GetWidth();   
    72.     int nHeight = cimage.GetHeight();   //重建mat       
    73.     if (1 == nChannels)   
    74.     {         
    75.         mat.create(nHeight, nWidth, CV_8UC1);  
    76.     }   else if(3 == nChannels)   
    77.     {         
    78.         mat.create(nHeight, nWidth, CV_8UC3);     
    79.     }   //拷贝数据    
    80.     uchar* pucRow;    
    81.     //指向数据区的行指针       
    82.     uchar* pucImage = (uchar*)cimage.GetBits();     //指向数据区的指针    
    83.     int nStep = cimage.GetPitch();                  //每行的字节数,注意这个返回值有正有负      
    84.     for (int nRow = 0; nRow < nHeight; nRow++)   
    85.     {         
    86.         pucRow = (mat.ptr<uchar>(nRow));   
    87.         for (int nCol = 0; nCol < nWidth; nCol++)      
    88.         {             
    89.             if (1 == nChannels)       
    90.             {                 
    91.                 pucRow[nCol] = *(pucImage + nRow * nStep + nCol);   
    92.             }             
    93.             else if (3 == nChannels)          
    94.             {                 
    95.                 for (int nCha = 0 ; nCha < 3; nCha++)   
    96.                 {                     
    97.                     pucRow[nCol * 3 + nCha] = *(pucImage + nRow * nStep + nCol * 3 + nCha);       
    98.                 }                         
    99.             }         
    100.         }         
    101.     }  
    102. }  

    上面的代码是我在一位大神的博客中找到的,实现了Mat和CImage之间的转换,这和QT中显示opencv的图片原理不谋而合,先以opencv的方式读取图片然后将其转换为CImage在显示到单文档上。如下图:



    1. // MFCApplication1View.h : CMFCApplication1View 类的接口  
    2. //  
    3.   
    4. #pragma once  
    5. #include<atlimage.h>  
    6. #include"CImageMat.h"  
    7. #include"PI.h"  
    8.   
    9. class CMFCApplication1View : public CView  
    10. {  
    11. protected// 仅从序列化创建  
    12.     CMFCApplication1View();  
    13.     DECLARE_DYNCREATE(CMFCApplication1View)  
    14.   
    15. // 特性  
    16. public:  
    17.     CMFCApplication1Doc* GetDocument() const;  
    18.   
    19. // 操作  
    20. public:  
    21.   
    22. // 重写  
    23. public:  
    24.     virtual void OnDraw(CDC* pDC);  // 重写以绘制该视图  
    25.     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);  
    26. protected:  
    27.     virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);  
    28.     virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);  
    29.     virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);  
    30.   
    31. // 实现  
    32. public:  
    33.     virtual ~CMFCApplication1View();  
    34. #ifdef _DEBUG  
    35.     virtual void AssertValid() const;  
    36.     virtual void Dump(CDumpContext& dc) const;  
    37. #endif  
    38.   
    39. private:  
    40.     CImage image;  
    41.     Mat pic;  
    42.     std::string str;  
    43.   
    44. protected:  
    45.   
    46. // 生成的消息映射函数  
    47. protected:  
    48.     DECLARE_MESSAGE_MAP()  
    49. public:  
    50.     afx_msg void OnOpenImage();  
    51.     afx_msg void OnImageGray();  
    52.     afx_msg void Onrefresh();  
    53.     afx_msg void OnHelp();  
    54.     afx_msg void OnSaveImgae();  
    55.     afx_msg void OnAuthor();  
    56.     afx_msg void OnThreshold();  
    57.     afx_msg void OnSplit();  
    58. };  
    59.   
    60. #ifndef _DEBUG  // MFCApplication1View.cpp 中的调试版本  
    61. inline CMFCApplication1Doc* CMFCApplication1View::GetDocument() const  
    62.    { return reinterpret_cast<CMFCApplication1Doc*>(m_pDocument); }  
    63. #endif  


    1. // MFCApplication1View.cpp : CMFCApplication1View 类的实现  
    2. //  
    3.   
    4. #include "stdafx.h"  
    5. // SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的  
    6. // ATL 项目中进行定义,并允许与该项目共享文档代码。  
    7. #ifndef SHARED_HANDLERS  
    8. #include "MFCApplication1.h"  
    9. #endif  
    10.   
    11. #include "MFCApplication1Doc.h"  
    12. #include "MFCApplication1View.h"  
    13.   
    14. #ifdef _DEBUG  
    15. #define new DEBUG_NEW  
    16. #endif  
    17.   
    18.   
    19. // CMFCApplication1View  
    20.   
    21. IMPLEMENT_DYNCREATE(CMFCApplication1View, CView)  
    22.   
    23. BEGIN_MESSAGE_MAP(CMFCApplication1View, CView)  
    24.     // 标准打印命令  
    25.     ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)  
    26.     ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)  
    27.     ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)  
    28.     ON_COMMAND(ID_32771, &CMFCApplication1View::OnOpenImage)  
    29.     ON_COMMAND(ID_32772, &CMFCApplication1View::OnImageGray)  
    30.     ON_COMMAND(ID_32773, &CMFCApplication1View::Onrefresh)  
    31.     ON_COMMAND(ID_32775, &CMFCApplication1View::OnHelp)  
    32.     ON_COMMAND(ID_32776, &CMFCApplication1View::OnSaveImgae)  
    33.     ON_COMMAND(ID_32777, &CMFCApplication1View::OnAuthor)  
    34.     ON_COMMAND(ID_Menu, &CMFCApplication1View::OnThreshold)  
    35.     ON_COMMAND(ID_32779, &CMFCApplication1View::OnSplit)  
    36. END_MESSAGE_MAP()  
    37.   
    38. // CMFCApplication1View 构造/析构  
    39.   
    40. CMFCApplication1View::CMFCApplication1View()  
    41. {  
    42.     // TODO: 在此处添加构造代码  
    43.   
    44. }  
    45.   
    46. CMFCApplication1View::~CMFCApplication1View()  
    47. {  
    48. }  
    49.   
    50. BOOL CMFCApplication1View::PreCreateWindow(CREATESTRUCT& cs)  
    51. {  
    52.     // TODO: 在此处通过修改  
    53.     //  CREATESTRUCT cs 来修改窗口类或样式  
    54.   
    55.     return CView::PreCreateWindow(cs);  
    56. }  
    57.   
    58. // CMFCApplication1View 绘制  
    59.   
    60. void CMFCApplication1View::OnDraw(CDC* pDC)  
    61. {  
    62.     CMFCApplication1Doc* pDoc = GetDocument();  
    63.     ASSERT_VALID(pDoc);  
    64.     if (!pDoc)  
    65.         return;  
    66.   
    67.     // TODO: 在此处为本机数据添加绘制代码  
    68.     if (!image.IsNull())  
    69.     {  
    70.         image.Draw(pDC->GetSafeHdc(), 0, 0);  
    71.     }  
    72. }  
    73.   
    74.   
    75. // CMFCApplication1View 打印  
    76.   
    77. BOOL CMFCApplication1View::OnPreparePrinting(CPrintInfo* pInfo)  
    78. {  
    79.     // 默认准备  
    80.     return DoPreparePrinting(pInfo);  
    81. }  
    82.   
    83. void CMFCApplication1View::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)  
    84. {  
    85.     // TODO: 添加额外的打印前进行的初始化过程  
    86. }  
    87.   
    88. void CMFCApplication1View::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)  
    89. {  
    90.     // TODO: 添加打印后进行的清理过程  
    91. }  
    92.   
    93.   
    94. // CMFCApplication1View 诊断  
    95.   
    96. #ifdef _DEBUG  
    97. void CMFCApplication1View::AssertValid() const  
    98. {  
    99.     CView::AssertValid();  
    100. }  
    101.   
    102. void CMFCApplication1View::Dump(CDumpContext& dc) const  
    103. {  
    104.     CView::Dump(dc);  
    105. }  
    106.   
    107. CMFCApplication1Doc* CMFCApplication1View::GetDocument() const // 非调试版本是内联的  
    108. {  
    109.     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMFCApplication1Doc)));  
    110.     return (CMFCApplication1Doc*)m_pDocument;  
    111. }  
    112. #endif //_DEBUG  
    113.   
    114.   
    115. // CMFCApplication1View 消息处理程序  
    116.   
    117.   
    118. void CMFCApplication1View::OnOpenImage()  
    119. {  
    120.     // TODO: 在此添加命令处理程序代码  
    121.     CFileDialog dlg(TRUE/*, _T(".bmp"), _T("*.bmp"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("位图文件(*.bmp)|*.bmp | JEPG文件(*.jpg) | *.jpg || ")*/);  
    122.     if (dlg.DoModal() == IDOK)  
    123.     {  
    124.         if (!image.IsNull())  
    125.         {  
    126.             image.Destroy();  
    127.         }  
    128.         image.Load(dlg.GetPathName());  
    129.         //CString cstr = dlg.GetPathName();  
    130.         str = CStringA(dlg.GetPathName());  
    131.         pic = imread(str, CV_LOAD_IMAGE_COLOR);  
    132.         Invalidate();  
    133.     }  
    134. }  
    135.   
    136.   
    137. void CMFCApplication1View::OnImageGray()  
    138. {  
    139.     // TODO: 在此添加命令处理程序代码  
    140.     //CImage2Mat(image, pic);  
    141.     if (pic.channels() == 1) {  
    142.         AfxMessageBox(_T("怕是已经灰度化过了"));  
    143.         return;  
    144.     }  
    145.     //cvtColor(pic, pic, CV_BGR2GRAY);  
    146.     pi_gray(pic, pic);  
    147.     Mat2CImage(pic, image);  
    148.     Invalidate();  
    149. }  
    150.   
    151.   
    152. void CMFCApplication1View::Onrefresh()  
    153. {  
    154.     // TODO: 在此添加命令处理程序代码  
    155.     pic = imread(str, CV_LOAD_IMAGE_COLOR);  
    156.     Mat2CImage(pic, image);  
    157.     Invalidate();  
    158. }  
    159.   
    160.   
    161. void CMFCApplication1View::OnHelp()  
    162. {  
    163.     // TODO: 在此添加命令处理程序代码  
    164.     AfxMessageBox(_T("VS2017下的单文档基于Opencv3.2的基本图形处理!"));  
    165. }  
    166.   
    167.   
    168. void CMFCApplication1View::OnSaveImgae()  
    169. {  
    170.     // TODO: 在此添加命令处理程序代码  
    171.     CFileDialog dlg(FALSE);  
    172.     {  
    173.         if (dlg.DoModal() == IDOK)  
    174.         {  
    175.             string savePath = CStringA(dlg.GetPathName());  
    176.             imwrite(savePath, pic);  
    177.             string info = "图片已存储到:" + savePath + "了!";  
    178.             CString cstr(info.c_str());  
    179.             AfxMessageBox(cstr);  
    180.         }  
    181.     }  
    182. }  
    183.   
    184.   
    185. void CMFCApplication1View::OnAuthor()  
    186. {  
    187.     // TODO: 在此添加命令处理程序代码  
    188.     AfxMessageBox(_T("作者:pedro\n时间:2017.4.15"));  
    189. }  
    190.   
    191.   
    192. void CMFCApplication1View::OnThreshold()  
    193. {  
    194.     if (pic.channels() > 1)  
    195.     {  
    196.         AfxMessageBox(_T("怕是没有输入单通道图像!"));  
    197.         return;  
    198.     }  
    199.     // TODO: 在此添加命令处理程序代码  
    200.     threshold(pic, pic, 0, 255, THRESH_OTSU);  
    201.     Mat2CImage(pic, image);  
    202.     Invalidate();  
    203. }  
    204.   
    205.   
    206. void CMFCApplication1View::OnSplit()  
    207. {  
    208.     // TODO: 在此添加命令处理程序代码  
    209.     if (pic.channels() < 3)  
    210.     {  
    211.         AfxMessageBox(_T("怕是没有输入彩色图像!"));  
    212.         return;  
    213.     }  
    214.     vector<Mat> mats;  
    215.     split(pic, mats);  
    216.     imshow("Blue", mats[0]);  
    217.     imshow("Green", mats[1]);  
    218.     imshow("Red", mats[2]);  
    219. }  
    展开全文
  • mfc opencv读取并显示图像
  • 先说一下我理解的原理,opencv首先通过读取摄像头的一帧画面,显示到picture控件里,然后启动定时器,每隔一段时间触发一次,每次都读取一帧显示到picture控件中。 使用定时器需要做以下步骤: 1.打开定时器 ...
  • 开发工具vs2017,opencv+mfc,可以读取bmp图片文件,然后对图片进行加入噪声,画取轮廓,像素过滤等操作
  • Opencv概述

    2015-07-29 11:00:24
    opencv是什么 OpenCV是一个用于图像处理、分析、机器视觉方面的开源函数库.   无论你是做科学研究,还是商业应用,opencv都可以作为你理想的工具库,因为,对于这两者,它完全是免费的。 该库采用C及C++语言...
  • opencv是什么

    2011-05-02 15:27:00
    OpenCV是一个用于图像处理、分析、机器视觉方面的开源函数库. 无论你是做科学研究,还是商业应用,opencv都可以作为你理想的工具库,因为,对于这两者,它完全是免费的。该库采用C及C++语言编写,可以在windows, ...
  • OpenCV学习

    2018-07-24 19:19:01
    另一篇参考文献: 中文手册 ...的番外篇,因为严格来说不是在讲Python而是讲在Python下使用OpenCV。本篇将介绍和深度学习数据处理阶段最相关的基础使用,并完成4个有趣实用的小例子: - 延时摄影小程序 - 视频中...
  • 目录 1 背景 ...在本教程中,我们将了解OpenCV 3中引入的OpenCV目标跟踪API。我们将学习如何以及何时使用OpenCV 3中提供的8种不同的跟踪器BOOSTING,MIL,KCF,TLD,MEDIANFLOW,GOTURN,MOSS...
  • 一、Python OpenCV 入门欢迎阅读系列教程,内容涵盖 OpenCV,它是一个图像和视频处理库,包含 C ++,C,Python 和 Java 的绑定。 OpenCV 用于各种图像和视频分析,如面部识别和检测,车牌阅读,照片编辑,高级机器人...
  • opencv概述及安装步骤

    2014-04-13 21:52:13
    本科时对于Opencv这个名词只闻其名,一直不识庐山面目,也没想过以后有一天会用到它。近日由于课题原因,我只能拿出学长推荐给我那本《学习opencv》开始零基础...一) opencv概述 1)OpenCV的全称是:OpenSource Comp
  • 一、OPenCV的概念和结构OpenCV:开源的计算机视觉库,一般用C和C++ 编写OpenCV的结构共分为五个部分,分别是图像处理和视觉算法(CV)、机器学习库(ML)、图像和视频输入/输出库(HighGUI)、基本结构和算法,XML...
  • 一:OpenCV概述 1.opencv相关概念 图像处理 利用计算机对图像进行分析处理,达到所需结果的技术,一般指的是数字图像处理,通过数码设备得到的数字图像是一个很大的二维数组,数组的元素叫像素,像素的值叫灰度值...
  • OpenCV概述

    2011-09-13 15:06:21
    Intel® 开源计算机视觉库OpenCV 目录  [隐藏] 1 什么是OpenCV2 重要特性3 谁创建了它4 新特征5 从哪里下载 OpenCV6 如果在安装/运行/使用 OpenCV 中遇到问题7 OpenCV参考手册8 中文...
  • OpenCV概述和安装

    2020-04-19 16:11:12
    OpenCV概述 OpenCV 的理论支撑来源于《数字图像处理》,数字图像处理使用的都是一些比较传统的算法,而目前也添加了一些比较前沿的算法,虽然有深度学习算法,但是主流不是深度学习算法。【更新速度也没有深度学习...
1 2 3 4 5 ... 20
收藏数 3,978
精华内容 1,591
关键字:

opencv