OpenCV教程 之 基础操作:图像的读入、输出、访问、Mat类、色彩缩减与线性融合

这里总结了一点关于OpenCV3版本的简单操作、供初学者快速入门。过程中会对代码分块逐个讲解,并给出测试结果图

一、OpenCV简介

OpenCV,是一个基于开源发行的跨平台计算机视觉库,它实现了图像处理和计算机视觉方面的很多通用的算法,已经成为了计算机视觉领域最有力的研究工具之一。其全称是Open Source Computer Vision Library,直译就是开源计算机视觉库,OpenCV由一系列C函数和C++类构成,轻量且高效,强大的OpenCV除了用C/C++语言进行开发和使用之外,还支持使用C#、Ch、Ruby等编程语言,同时提供了对Python、Ruby、MATLAB等语言的接口,通用性强。本文所用的语言为C++,在eclipse-Mac环境下开发,代码基本兼容Windows系统

![这里写图片描述](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcxMDA2MjMxMjU3Nzc4?x-oss-process=image/format,png) OpenCV图标

二、图像的读入与输出

作为最基本的图像操作,读入和输出这里需要介绍两个非常实用的基本函数imread还有imshow

2.1 imread

	//imread的函数原型
      Mat imread(const string& filename, int flags = 1);
      
	(1)第一个参数,const string&类型的filepath,填我们需要载入的图片路径名
	   imread函数支持读取的常用文件类型有:
	   
	   > Windows位图: *.bmp,*.dib
	   >JPEG文件:     *.jpeg,*jpg,*jpe
	   >JPEG 2000文件:*.jp2
	   >PNG图片:      *.png
	   >... 
	  
	(2)第二个参数,int类型的flags,为载入标识,它指定一个加载图像的颜色类型,自带默认值为1
	   其备选参数和意义为:

       >0 : 将原图准换为灰度图像读取
       >1 : 返回一个3通道的彩色图像
       >2 : 如果读入的图像深度为16位活着32位,则返回对应深度的图像,否则转换为8位图像再返回
       >4 : 读取任意色彩模式的图像,根据原图
       
       ps :flags可以不取以上枚举值,当
       flags > 0时,等价于1
       flags < 0时,返回包含Alpha通道的加载图像

2.2 imshow

     //imshow的函数原型
	 void imshow(const string& winname,InputArray mat);

     (1)第一个参数:const string&类型的winname,需要填写显示的窗口标识名称

     (2)第二个参数:InputArray 类型的mat,填写需要显示的图像

2.3 应用举例

#include <opencv2/opencv.hpp>
using namespace cv;

int main(){

	Mat g_srcImage1 = imread("/Users/zhuxiaoxiansheng/Desktop/lalaland_1.jpeg",0);
	resize(g_srcImage1,g_srcImage1,Size(1000,640));  //设定图像大小
	imshow("flags = 0",g_srcImage1);                 //显示图像
	waitKey();                                       //等待任意键输入
	
	Mat g_srcImage2 = imread("/Users/zhuxiaoxiansheng/Desktop/lalaland_1.jpeg",1);
	resize(g_srcImage2,g_srcImage2,Size(1000,640));
	imshow("flags = 1",g_srcImage2);
	waitKey();
	
	Mat g_srcImage3 = imread("/Users/zhuxiaoxiansheng/Desktop/lalaland_1.jpeg",2);
	resize(g_srcImage3,g_srcImage3,Size(1000,640));
	imshow("flags = 2",g_srcImage3);
	waitKey();
	
	Mat g_srcImage4 = imread("/Users/zhuxiaoxiansheng/Desktop/lalaland_1.jpeg",4);
	resize(g_srcImage4,g_srcImage4,Size(1000,640));
	imshow("flags = 4",g_srcImage4);
	waitKey();
	
	return 0;
}

测试结果

这里写图片描述
这里写图片描述
这里写图片描述

三、图像的基本管理:Mat类

自从OpenCV跨入了2.0时代,Mat类数据结构便成为了图像处理的主打类型,Mat类由两部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针。矩阵头的尺寸是一个常数值,但矩阵本事身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸数大数个数量级。

在解决问题中,传递图像是常有的事,但图像的复制会造成很大的计算开销与空间损失,因此,OpenCV使用了引用计数机制,其思路是让每个Mat对像有自己的信息头,但共享一个矩阵,接下来简单介绍一下Mat类型的创建,和元素访问

3.1 Mat类的创建

这里介绍几种常用的Mat创建方法

	(1)使用Mat的构造函数
	 Mat M(2,2,CV_8UC3,Scalar:all(0));
	 cout << M << endl;
     
     上式中CV_8UC3是指使用8位的unsigned char型,每个像素由三个元素组成三通道
     Scalar是一个short型向量,能用指定的定制化值来初始化矩阵,上式的输出为
     
     [  0,   0,   0,   0,   0,   0;
	    0,   0,   0,   0,   0,   0]
	 
	 (2)采用Matlab的初始化方式
	 Mat E = Mat::eye(4,4,CV_64F);
     cout << "E = " << endl << E << endl << endl;

     Mat O = Mat::ones(2,2,CV_32F);
     cout << "O = " << endl << O << endl << endl;

     Mat Z = Mat::zeros(3,3,CV_8UC1);
     cout << "Z = " << endl << Z << endl << endl;

     上式的输出为:
     E = 
	   [1, 0, 0, 0;
	    0, 1, 0, 0;
	    0, 0, 1, 0;
	    0, 0, 0, 1]

	 O = 
	   [1, 1;
	    1, 1]

	 Z = 
	   [0,   0,   0;
	    0,   0,   0;
	    0,   0,   0]

3.2 Mat类的元素值访问

这一部分可以简单的用一个swich代表来表述,示例如下:

    switch(img.channels())              //根据图像的通道数作出不同的选择
{
    case 1:
    {for(int i=0;i<img.rows;i++)
      {
        for(int j=0;j<img.cols;j++)
            {
                img.at<uchar>(i,j)=0;   //访问单通道元素
            }
       }
    break;
     }    
    case 3:
    {
        for(int i=0;i<img.rows;i++)
        {
            for(int j=0;j<img.cols;j++)
            {
                img.at<Vec3b>(i,j)[0]=0;    //访问三通道元素
                img.at<Vec3b>(i,j)[1]=0;
                img.at<Vec3b>(i,j)[2]=0;
            }
        }
    break;
    }
}

3.3 应用举例

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;

int main(){

	Mat A(2,2,CV_8UC3),B,C;     //定义A矩阵,设定其结构为2X2,3通道颜色,并声明B,C
	randu(A,0,255);             //给A随机赋值
	B = A;                      //引用
	A.copyTo(C);                //复制

	cout << "A = " << endl << A << endl;
	cout << "B = " << endl << B << endl;
	cout << "C = " << endl << C << endl <<endl;

	A.at<Vec3b>(1,1)[0] = 0;    //改变A的(1,1)元素三个通道的值
	A.at<Vec3b>(1,1)[1] = 0;
	A.at<Vec3b>(1,1)[2] = 0;

	cout << "A = " << endl << A << endl;
	cout << "B = " << endl << B << endl;
	cout << "C = " << endl << C << endl;

	return 0;
}

显示结果

A = 
[ 91,   2,  79, 179,  52, 205;
 236,   8, 181, 239,  26, 248]
B = 
[ 91,   2,  79, 179,  52, 205;
 236,   8, 181, 239,  26, 248]
C = 
[ 91,   2,  79, 179,  52, 205;
 236,   8, 181, 239,  26, 248]

A = 
[ 91,   2,  79, 179,  52, 205;
 236,   8, 181,   0,   0,   0]
B = 
[ 91,   2,  79, 179,  52, 205;
 236,   8, 181,   0,   0,   0]
C = 
[ 91,   2,  79, 179,  52, 205;
 236,   8, 181, 239,  26, 248]

四、图像的色彩缩减

我们知道,若矩阵元素存储的是单通道像素,使用C或C++的无符号字符类型,那么像素可能有256个不同的值。但若是三通道图像,这种存储格式的颜色会达到一千六百多万种,用如此之多的颜色来进行处理,可能会对算法性能造成严重影响。

其实仅用这些颜色中最具有代表性的很小的部分,就足以达到相同的效果,颜色空间缩减的做法是:将现有的颜色空间值除以某个输入值,以获得较少的颜色数。比如,颜色0~9可以取值为0,10~19可以取值为10,依次类推,依据这种思想,我们可以实现如下代码

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void colorReduce(Mat& inputImage,Mat& outputImage,int div);

int main(){
	Mat srcImage = imread("/Users/zhuxiaoxiansheng/Desktop/lalaland_1.jpeg");
	resize(srcImage,srcImage,Size(1000*1.5,640*1.5));

    Mat dstImage;
    dstImage.create(srcImage.rows,srcImage.cols,srcImage.type());
    colorReduce(srcImage,dstImage,32);
    imshow("效果图",dstImage);
    waitKey(0);
	return 0;
}

void colorReduce(Mat& inputImage,Mat&outputImage,int div){
	outputImage = inputImage.clone();
	Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();
	Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();

	for(;it != itend;++it){
		(*it)[0] = (*it)[0]/div*div + div/2;
		(*it)[1] = (*it)[1]/div*div + div/2;
		(*it)[2] = (*it)[2]/div*div + div/2;
	}
}

效果图

这里写图片描述

五、两张图片的线形融合

两张图片的线形融合,在OpenCV里有一个非常实用的函数,接下来,先简单介绍一下这个函数

	//addWeighted的函数原型
		void addWeighted(InputArray src1,double alpha,InputArray  
		Src2,double beta,double gamma,OutputArray dst,int dtype = -1);
	
	>第一个参数,表示第一个需要加权的图像M
	>第二个参数,表示第一个数组的权重
	>第三个参数,表示第二个需要加权的图像,两个图像的大小与通道数必须相等
	>第四个参数,表示第二个图像的权重
	>第五个参数,一个加到总和上的标量值,影响合成图片的亮度
	>第六个参数,输出图像
	>第七个参数,输出图像的深度,默认-1,即和第一个输入图像相同

接下来在要做的演示中,我们用到的两张图如下

![这里写图片描述]这里写图片描述

演示代码为


#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;

#define WINDOW_NAME "【线性混合示例】"

const int g_nMaxAlphaValue = 100;
int g_nAlphaValueSlider;
double g_dAlphaValue;
double g_dBetaValue;

Mat g_srcImage1;
Mat g_srcImage2;
Mat g_dstImage;

void on_Trackbar(int ,void*){
	g_dAlphaValue = (double) g_nAlphaValueSlider/g_nMaxAlphaValue;
	g_dBetaValue = 1 - g_dAlphaValue;

	addWeighted(g_srcImage1,g_dAlphaValue,g_srcImage2,g_dBetaValue,0.0,g_dstImage);
	imshow(WINDOW_NAME, g_dstImage);
}

int main(int argc,char *argv[]){
	g_srcImage1 = imread("/Users/zhuxiaoxiansheng/Desktop/lalaland_1.jpeg");
	g_srcImage2 = imread("/Users/zhuxiaoxiansheng/Desktop/lalaland_2.jpg");
    resize(g_srcImage1,g_srcImage1,Size(1000*1.5,640*1.5));
	resize(g_srcImage2,g_srcImage2,Size(1000*1.5,640*1.5));
	if(!g_srcImage1.data){
		printf("读取第一幅图片错误,请确定目录下是否有imread函数指定图片存在!\n");
		return -1;
	}
	if(!g_srcImage2.data){
		printf("读取第二幅图片错误,请确定目录下是否有imread函数指定图片存在!\n");
		return -1;
	}

	g_nAlphaValueSlider = 70;
	namedWindow(WINDOW_NAME,1);
	char TrackbarName[50];
	sprintf(TrackbarName,"透明值 %d",g_nMaxAlphaValue);
	createTrackbar(TrackbarName,WINDOW_NAME,&g_nAlphaValueSlider,g_nMaxAlphaValue,on_Trackbar);
	on_Trackbar(g_nAlphaValueSlider,0);
	waitKey(0);
	return 0;
}

效果图:

这里写图片描述

六、更多资源下载

微信搜索“老和山算法指南”获取更多下载链接与技术交流群
在这里插入图片描述
有问题可以私信博主,点赞关注的一般都会回复,一起努力,谢谢支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Liangjun_Feng

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

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

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

打赏作者

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

抵扣说明:

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

余额充值