学习OpenCV-(1)-安装与初探

文章目录

  • 学习OpenCV-(1)-安装与初探
    • 1.概述
      • 下载和安装
      • OpenCV所拥有的模块
    • 2.初探
      • 1.显示图片
      • 2.显示视频
      • 3.简单的变换
      • 4.不那么简单的变换
      • 5.从摄像头中读取
      • 6.写入AVI文件

学习OpenCV-(1)-安装与初探

1.概述

OpenCV是用C和C++编写的开源计算机视觉库,可以在Windows、linux、Mac OS等系统上运行。同时它提供了Python、Java、Matlab等其他一些语言的接口,将库导入安卓和iOS中为移动设备开发应用。

OpenCV的一个目标是提供易于使用的计算机视觉接口,帮助人们快速建立精巧的视觉应用。OpenCV库包含从计算机视觉各个领域衍生出来的500多个函数,包括工业产品质量检验、医学图像处理、安保领域、交互操作、相机校正、双目视觉以及机器人学。因为计算机视觉经常和机器学习一起使用,所以OpenCV也包含一个完备的,具有通用性的机器学习库(ML模块)。

OpenCV的开源许可允许任何人利用OpenCV包含的任何组件构建商业产品。

下载和安装

在OpenCV的官方网站可以下载最新的且完整的源码及大部分release版本源码。但是我发现OpenCV的官网打不开,这时我们可以在github(https://github.com/opencv/opencv/releases)上下载exe文件。下载后打开安装很快就完成了。注意安装的时候记住安装路径。安装后在文件管理器打开它的安装文件。应该是如下图内容。

学习OpenCV-(1)-安装与初探_第1张图片

接下来就需要配置visual studio了。我用的是vs2019,不同版本的vs可能具体配置也不同,vs2017的用户可以参考https://blog.csdn.net/qq_41498261/article/details/83822094?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task来完成配置。

流程:

  1. 新建C++控制台应用
    学习OpenCV-(1)-安装与初探_第2张图片

  2. 将x86改为x64
    学习OpenCV-(1)-安装与初探_第3张图片

  3. 视图->属性管理器-> Debug|x64,右击添加新项目属性表,位置最好选择一个好找的位置,名字可以命名为 opencv.props。

  4. 双击添加的属性表开始配置。通用属性->VC++目录->包含目录->编辑,如下图
    学习OpenCV-(1)-安装与初探_第4张图片

选择库目录,编辑
学习OpenCV-(1)-安装与初探_第5张图片

  1. 链接器->输入->附加依赖项,输入opencv_world341d.lib
    学习OpenCV-(1)-安装与初探_第6张图片

  2. 这样就算配置玩了,测试一下代码能不能正常运行

#include 
#include
int main()
{
    cv::Mat img = cv::imread("Hakurei Reimu.png");//图片路径
    if (img.empty())return -1;
    cv::namedWindow("test", cv::WINDOW_AUTOSIZE);
    cv::imshow("test", img);
    cv::waitKey(0);
    cv::destroyWindow("test");
    return 0;
}

注意图片路径要是一个有效的路径,为了避免出错可以将你的一个本地图片复制粘贴到cpp源文件同目录下,这样你就可以直接输入文件名了。最后运行成功的话会出来一个窗口显示相应的图片。
学习OpenCV-(1)-安装与初探_第7张图片

属性表保存好了以后,再新建控制台应用只需要在属性管理器Debug|x64右键选择添加现有属性表添加前面新建的属性表就完事了。

OpenCV所拥有的模块

  • Core:基础结构以及基本操作
  • Improc:图像处理模块,包含基本的图像转换,包括滤波以及类似的卷积操作
  • Highgui(在OpenCV3.0中分割成imcodecs、videoio以及highgui三部分):包含用来显示图像或者简单的输入的用户交互函数。可以看作一个轻量级的Windows UI工具包
  • Video:包含读取和写视频流函数
  • Calib3d:包含校准单个、双目以及多个相机的算法实现
  • Feature2d:包含用于检验、描述以及匹配特征点的算法
  • Objectect:包含检测特定目标,如人脸或者行人的算法。可以训练检测器并用来检测其他物体。
  • M1:机器学习模块本身是一个非常完备的模块,包含大量的机器学习算法实现并且这些算法都能和OpenCV的数据类型自然交互
  • Flann:快速最近邻库。包含一些也许不会直接使用但其他模块中的函数会调用在数据集中进行最近邻搜索的算法
  • GPU(在OpenCV3.0中被分割成多个cuda*模块):函数在CUDA GPU上的优化实现
  • Photo:计算机摄影学的一些函数
  • Stitching:精巧的图像拼接流程实现
  • Nonfree(在OpenCV3.0中融合进了opencv_contrib/xfeatures2d):一些受到专利保护的或者受到使用限制的(比如SIFT算法)算法
  • Contrib(在OpenCV3.0中融合进了opencv_contrib):包含一些新的还没有被集成进OpenCV库的东西
  • Legacy(在OpenCV3.0被取消):包含一些老的还没有被取消的东西
  • 0cl(在OpenCV3.0被取消,取而代之的是T-API):可以认为它和GPU相似,实现了开放并行编程的Khronos OpenGL标准。

2.初探

1.显示图片

我们在来看一下前面的哪个测试程序

#include 
#include
int main()
{
    cv::Mat img = cv::imread("Hakurei Reimu.png");//图片路径
    if (img.empty())return -1;
    cv::namedWindow("test", cv::WINDOW_AUTOSIZE);
    cv::imshow("test", img);
    cv::waitKey(0);
    cv::destroyWindow("test");
    return 0;
}

首先OpenCV的函数都位于命名空间cv下,如果想省略cv::,可以在前面用using namespace cv。但这样存在与其他命名空间冲突的风险。

函数imread()根据文件名来决定图像格式的处理,也会自动申请图像需要的内存,它可以读取很多种图像格式,函数返回一个Mat结构,这个结构是OpenCV中你会接触的最多的自带结构,OpenCV用这个结构来处理所有类型的图像:单通道、多通道、整型、浮点数以及各种类型。

函数namedWindow由HighGUi模块提供,将一个名称赋予窗口。第二个参数说明了Windows的特性。默认情况下全部设置为0,也可以设置为WINDOW_AUTOSIZE,这样窗口的大小会和载入图像的大小一致,图像会被缩放以使用窗口。

通过imshow()来进行显示。imshow()将建一个窗口。(如果窗口不存在他会自动调用namedWindow()新建一个窗口)。调用imshow()时窗口将被重绘上要求的图片,并且窗口会自动调整大小(使用参数WINDOW_AUTOSIZE)。

waitkey(0)函数告诉系统暂停并等待键盘事件。如果传入参数大于0则会意味着程序等待的毫秒数,比如waitkey(100)就是等待100毫秒,如果100毫秒没有键盘事件就会继续执行程序。0或负数表示等待无限长时间。

最后用函数destroywindow()来让窗口自行销毁。

2.显示视频

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
int main(int argc, char** argv) {
	namedWindow("test", WINDOW_AUTOSIZE);
	VideoCapture cap;
	cap.open("灵梦.mp4");
	Mat frame;
	for (;;) {
		cap >> frame;
		if (frame.empty())break;
		imshow("test", frame);
		if (waitKey(33) >= 0)break;
	}
	return 0;
}

由于上面的程序无法在视频中进行跳转,所以我们需要添加一个滑动条来实现视频跳转。

下面就是一个稍微复杂的程序

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
#include
using namespace cv;
using namespace std;
int g_slider_position = 0;
int g_run = 1, g_dontset = 0;
VideoCapture g_cap;
void onTrackbarSlide(int pos, void*) {
	g_cap.set(CAP_PROP_POS_FRAMES, pos);
	if (!g_dontset)
		g_run = 1;
	g_dontset = 0;
}
int main(int argc, char** argv) {
	namedWindow("test", WINDOW_AUTOSIZE);
	g_cap.open("灵梦.mp4");
	int frames = (int)g_cap.get(CAP_PROP_FRAME_COUNT);
	int tmpw = (int)g_cap.get(CAP_PROP_FRAME_WIDTH);
	int tmph = (int)g_cap.get(CAP_PROP_FRAME_HEIGHT);
	cout << "Video has " << frames << " frames of dimension (" << tmpw << " , " << tmph << " )." << endl;
	createTrackbar("Position", "test", &g_slider_position, frames, onTrackbarSlide);
    //不难发现传入的参数分别是 --> name(命名),窗口,事件类型,事件相关联的响应函数
	Mat frame;
	for (;;) {
		if (g_run != 0) {
			g_cap >> frame;
			if (frame.empty())break;
			int current_pos = (int)g_cap.get(CAP_PROP_POS_FRAMES);
			g_dontset = 1;
			setTrackbarPos("Position", "test", current_pos);
			imshow("test", frame);
			g_run -= 1;
		}
		char c = (char)waitKey(10);
		if (c == 's') {
			g_run = 1;
			cout << "Single step,run= " << g_run << endl;
		}
		if (c == 'r') {
			g_run = -1;
			cout << "Run mode, run= " << g_run << endl;
		}
		if (c == 27)break;//ESC
	}
	return 0;
}

它允许用户按下s键来执行单步模式,按下r键恢复连续视频播放模式。然和时候都可以通过滑动条跳转到视频一个新的时间点,我们用单步模式在那个时间点播放。

程序中用createTrackbar()来创建一个滑动条。这些代码理解起来没有太大困难。

3.简单的变换

接下来我们会对视频中的每一帧实现一些简单的操作。

一个最简单的操作就是对图像的平滑处理,通过高斯核或者其他核卷积效减小图像的信息量。

如下代码在第一个代码的基础上做了一下修改,得到两个窗口,一个显示原图,一个显示处理后的图(图像被5*5大小的高斯核模糊并且被写入out变量中)。高斯核的大小必须是奇数。程序执行了两次模糊操作

#include 
#include
int main()
{
    cv::namedWindow("in", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("out", cv::WINDOW_AUTOSIZE);
    cv::Mat img = cv::imread("Hakurei Reimu.png");//图片路径
    if (img.empty())return -1;
    cv::imshow("in", img);
    cv::Mat out;
    cv::GaussianBlur(img, out, cv::Size(5, 5), 3, 3);
    cv::GaussianBlur(out, out, cv::Size(5, 5), 3, 3);
    cv::imshow("out", out);
    cv::waitKey(0);
    return 0;
}

学习OpenCV-(1)-安装与初探_第8张图片

4.不那么简单的变换

这里我们先用pyrDown()来创建一个新的图像,其宽高均为原始图像的一半

#include 
#include
int main()
{
    cv::namedWindow("in", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("out", cv::WINDOW_AUTOSIZE);
    cv::Mat img1 = cv::imread("Hakurei Reimu.png");//图片路径
    if (img1.empty())return -1;
   
    cv::Mat img2;
    cv::imshow("in", img1);
    cv::pyrDown(img1, img2);
    cv::imshow("out", img2);
    cv::waitKey(0);
    return 0;
}

学习OpenCV-(1)-安装与初探_第9张图片

下面我们用Canny边缘检测器输出一个单通道的(灰色)图像,其中边缘检测器通过cvtColor()函数生成一个和原图一样大小但只有一个通道的图像,从而将图像从BGR图像转换为灰度图,这个操作在OpenCV中定义为宏COLOR_BGR2GRAY。

#include 
#include
int main()
{
    cv::namedWindow("gray", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("canny", cv::WINDOW_AUTOSIZE);
    cv::Mat img1 = cv::imread("Hakurei Reimu.png");//图片路径
    if (img1.empty())return -1;
   
    cv::Mat img_gry,img_cny;
    cv::cvtColor(img1, img_gry, cv::COLOR_BGR2GRAY);
    cv::imshow("gray", img_gry);
    cv::Canny(img_gry, img_cny, 10, 100, 3, true);
    cv::imshow("canny", img_cny);
    cv::waitKey(0);
    return 0;
}

学习OpenCV-(1)-安装与初探_第10张图片

这里我们可以将这些操作组合起来,比如对图像收缩两次再寻找其边缘。然后通过一个简单的方法来读取结果的像素值

#include 
#include
int main()
{
    cv::namedWindow("gray", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("canny", cv::WINDOW_AUTOSIZE);
    cv::Mat img1 = cv::imread("Hakurei Reimu.png");//图片路径
    if (img1.empty())return -1;
   
    cv::Mat img_gry,img_cny,pyr,pyr2;
    cv::pyrDown(img1, pyr);
    cv::pyrDown(pyr, pyr2);
    cv::cvtColor(pyr2, img_gry, cv::COLOR_BGR2GRAY);
    cv::imshow("gray", img_gry);
    cv::Canny(img_gry, img_cny, 10, 100, 3, true);
    cv::imshow("canny", img_cny);
    int x = 16, y = 32;
    cv::Vec3b intensity = img1.at<cv::Vec3b>(y, x);
    uchar blue = intensity[0];
    uchar green = intensity[1];
    uchar red = intensity[2];
    std::cout << "At(x,y)=(" << x << "," << y << "):(blue,green,red)=("
        << (unsigned int)blue << ", " << (unsigned int)green << " , " << (unsigned int)red
        << ")" << std::endl;
    std::cout << "Gray pixel there is:" << (unsigned int)img_gry.at<uchar>(y, x) << std::endl;
    x /= 4; y /= 4;
    std::cout << "Pyramid2 pixel there is" << (unsigned int)pyr2.at<uchar>(y, x) << std::endl;
    img_cny.at<uchar>(x, y) = 128;

    cv::waitKey(0);
    return 0;
}

5.从摄像头中读取

int main(int argc,char** argv)
{
    cv::namedWindow("test", cv::WINDOW_AUTOSIZE);
    cv::VideoCapture cap;
    if (argc == 1) {
        cap.open(0);
    }
    else {
        cap.open(argv[1]);
    }
    if (!cap.isOpened()) {
        std::cerr << "could not open capture" << std::endl;
        return -1;
    }
    cv::waitKey(0);
    return 0;
}

同一个对象可以读取时评文件也可以连接摄像头。

连接一个摄像头需要给它一个相机ID号(如果只有一个摄像头连接,这个ID通常为0),ID的默认值为-1,这意味着随意选择一个。

6.写入AVI文件

我们可以创建一个写入对象将帧一次输入到一个视频文件中,通过对象VideoWriter。完成后调用VideoWriter.release()方法。

下面的这个程序会打开一个视频文件读取它的内容后将其转换为对数极坐标形式,然后将这种形式写入一个新的视频文件。

#include
#include
int main(int argc, char** argv) {
	cv::namedWindow("test", cv::WINDOW_AUTOSIZE);
	cv::namedWindow("log", cv::WINDOW_AUTOSIZE);
	cv::VideoCapture cap("灵梦.mp4");
	double fps = cap.get(cv::CAP_PROP_FPS);
	cv::Size size(
		(int)cap.get(cv::CAP_PROP_FRAME_WIDTH), (int)cap.get(cv::CAP_PROP_FRAME_HEIGHT)
	);
	cv::VideoWriter writer;
	writer.open("leimo.mp4", CV_FOURCC('M', 'J', 'P', 'G'), fps, size);
	cv::Mat log, bgr;
	for (;;) {
		cap >> bgr;
		if (bgr.empty())break;
		cv::imshow("test", bgr);
		cv::logPolar(bgr, log, cv::Point2f(bgr.cols / 2, bgr.rows / 2), 40, cv::WARP_FILL_OUTLIERS);
		cv::imshow("log", log);
		writer << log;
		char c = cv::waitKey(10);
		if (c == 27)break;
	}
	cap.release();
}

学习OpenCV-(1)-安装与初探_第11张图片---------------------------------------------------
参考:《学习OpenCV3》

你可能感兴趣的:(笔记,opencv,c++,计算机视觉)