2、图像处理基础

回顾

Mat

行 列 通道  维度

row col channel dim

在opencv里面的维度一定是大于等于2的,比如上面的一个行向量就是一个1*7的“二维向量”。

在Mat里面除了上面四个比较常用的量以外,还有flag变量,它是在Mat 的头里面,包含了图像的维度,深度(是short类型、char类型还是double类型的);除了flag还有一个data的指针,它是指向内存中数据所在地址的;还有一个refcount指针,引用计数指针,看有多少指针指向了这个地址。

第二讲的内容开始

在Mat中有一个data指针,data指针指向的是数据在内存中的首地址,

M.step[1]为每一行的列元素的大小!!!

由于上面的行和列表达起来有歧义,故其row和col都表示为-1.上面的矩阵为3*4*6的矩阵。

上面的step[0]为一个面的大小,为4*6*(4*16/8)=192bytes

step[1]为6*(4*16/8)=48

step[2]为4*(16/8)=8

例子:

Mat m(400,400,CV_8U,Scalar(0));

定义一个单通道的400*400大小的矩阵,矩阵元素初值为0,即全黑的图像

#include <iostream>

 #include<highgui.h> 

#include "opencv2/opencv.hpp" 

using namespace cv; 

using namespace std;

int main(int argc, char* argv[])

{

Mat m(400,400,CV_8U,Scalar(0));
 for (int col = 0; col < 400; col++)
 {
  for (int row = 195; row < 205; row++)
  {
   cout << (int)(*(m.data + m.step[0] * row + m.step[1] * col)) << "  ==>";
   //获取第[row,col]个像素点的地址并用*符号解析
   *(m.data + m.step[0] * row + m.step[1] * col) = 255;
   cout << (int)(*(m.data + m.step[0] * row + m.step[1] * col)) << endl;
  }
 }
 imshow("canvas",m);
 cvWaitKey();
 getchar();
 return 0;
}
View Code

 

例子:

Vec3i color定义一个变量,它是Vector类型的,有3个元素,且是int型的;

注意cvWaitKey()可以传参,单位是毫秒;

#include <iostream> #include<highgui.h> 

#include "opencv2/opencv.hpp" 

using namespace cv;

 using namespace std;

int main(int argc, char* argv[])

 {

Mat m = imread("lena.jpg");
 int *p_address;
 Vec3i color;
 for (int col = 20; col < 40; col++)
 {
  for (int row = 2; row < 20; row++)
  {
   color[0] = (int)(*(m.data+m.step[0]*row+m.step[1]*col));
   color[1] = (int)(*(m.data + m.step[0] * row + m.step[1] * col+m.elemSize1()));
   color[2] = (int)(*(m.data + m.step[0] * row + m.step[1] * col + m.elemSize1()*2));
   //获取第[row,col]个像素点的地址并用*符号解析
   cout << color[0] << "," << color[1] << "," << color[2] << "  ==>";
   color[0] = 255;
   color[1] = 0;
   color[2] = 0;
   *(m.data + m.step[0] * row + m.step[1] * col) = color[0];
   *(m.data + m.step[0] * row + m.step[1] * col + m.elemSize1()) = color[1];
   *(m.data + m.step[0] * row + m.step[1] * col + m.elemSize1() * 2) = color[2];
   cout << (int)(*(m.data + m.step[0] * row + m.step[1] * col)) << (int)(*(m.data + m.step[0] * row + m.step[1] * col + 1)) << (int)(*(m.data + m.step[0]*row + m.step[1] * col + 2)) << endl;
  }
 }
 imshow("canvas",m);
 cvWaitKey();
 getchar();
 return 0;
}

 

结果:

上面的数据类型是错误的!!!!!

opencv的模板类Mat_类

例子:

#include <iostream>

 #include<highgui.h>

 #include "opencv2/opencv.hpp" 

using namespace cv; 

using namespace std;

int main(int argc, char* argv[]) {

Mat m = imread("lena.jpg");
 Mat_<Vec3b> m2 = m;
 //for()循环画一个红色的实心圆
 for (int y = 21; y < 42; y++)
 {
  for (int x = 2; x < 21; x++)
  {
   if (pow(double(x - 11), 2) + pow(double(y - 31), 2) - 64.0 < 0.00000000001)
   {
    //Mat_模板类实现了对()的重载,可以定位到一个像素
    m2(x, y) = Vec3b(0,0,255);
   }
  }
 }imshow("canvas",m);
 cvWaitKey();
 getchar();
 return 0;
}
View Code

 

结果如下:

注意pow函数的含义:pow(src,double p,dst);//如果p是整数dst(I)=src(I)^p;其他|src(I)|^p

像素值的读写6

上面是进行图像的量化,把原来的像素点(之前是0-255),现在不需要那么高的像素级,把它降低10个等级,原来的像素级除以10再乘以10;

查找表,在上面先定义了一个uchar型的数组,uchar table[256];然后再进行像素量化降级,可以对比用二值图像来理解,然后再建立一个1*256的一个数组lookuptable。LUT(源图像输入,查找表,输出图像)。它只有在有映射关系的时候才能用到,是一个特例。

 

在2.4以前,有这两种数据类型IplImage和CvMat,我们现在都用Mat,然后有一些例程,它的形式是IplImage *p,这种图像指针类型的,我们可以用&iplimg这种引用的方式转换为Mat类型。

同时也支持这两种方式互转,

关于图像的读取

Mat imread(const string &filename,int flags=1)

imread()函数,返回值是一个Mat类型,它读取失败里面的*data是指向NULL的,建议不要使用指针来判断是否为NULL来说明是否读取成功;它自己有一个成员函数来判断的isempty();

flags只要大于0就代表返回3通道图像,灰度图像就会强制转化为3通道,flag为0,强制转化为单通道;flag小于0,那么是什么就读什么,不进行转换;

有一种图像格式叫做PNM,PPM,PGM,可移植像素格式,它是纯文本的格式,常见的是PGM的格式;

imwrite()是存储文件

注意并不是所有的Mat都可以存储成图像,目前只支持8U的单通道或者3通道格式,如果想把16U的转换成功的话,就只能用PNG格式或者JPEG2000,注意一下。

如果这个文件夹下有这个文件,它是直接覆盖的,不会有任何提醒;

首先来看看imwrite()函数的具体用法。

  bool imwrite(const string& filename, InputArray img, const vector<int>& params=vector<int>() )

  该函数是把程序中的Mat类型的矩阵保存为图像到指定位置。

  参数filename为所需保存图像的文件目录和文件名。这里的文件名需要带有图像格式后缀的,目前OpenCV该函数只支持JPEG,PNG,PPM,PGM,PBM,TIFF等。并不是所有Mat类型都支持。

  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中。

  参数params是用来设置对应图片格式的参数的,因为一般情况下这些图片格式都是经过了压缩的,这里就是设置这些压缩参数来控制图片的质量。该参数是一个vector<int>类型,里面分别存入paramId_1, paramValue_1, paramId_2, paramValue_2, ... 也就是说存入一对属性值。如果不设置该参数的话,则程序会自动根据所保存的图像格式采用一个默认的参数。

介绍一下,打开外设,或者读取视频的一个例子

VideoCapture这样一个类,有一个构造函数直接指定设备的编号cap(0);注意在工程的时候通过hub连接两个或者更多的时候,其ID可能就会是同一个,出现打不开的情况。

#include<iostream>
#include"opencv2/opencv.hpp"
#include<stdio.h>
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
 //打开摄像头
 VideoCapture cap(0);
 //打开视频文件 
 //VideoCapture cap("video.short.raw.avi");
 //检查是否成功打开
 if (!cap.isOpened())
 {
  cerr << "can not open a camera or file."<<endl;
  return -1;
 }
 Mat edges;
 //创建窗口
 namedWindow("edges",1);
 for (;;)
 {
  Mat frame;
  //从cap中读一帧,存到frame
  cap >> frame;
  //如果未读到图像
  if (frame.empty())
   break;
  //将读到的图像转为灰度图
  cvtColor(frame,edges,CV_BGR2GRAY);
  //进行边缘提取操作
  Canny(edges,edges,0,30,3);
  //显示结果
  imshow("edges",edges);
  //等待30s,如果按键则退出循环
  if (waitKey(30) >= 0)
   break;
 }
 //退出时会自动释放 cap 中占用资源 
 return 0;
}
View Code

 

上面的例子是读下面的例子是写

#include<iostream>
#include"opencv2/opencv.hpp"
#include<stdio.h>
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
 //定义视频的宽度和高度
 Size s(320, 240);
 //创建writer,并制定FOURCC及FPS等参数
 VideoWriter writer = VideoWriter("myvedio.avi",CV_FOURCC('M','J','P','G'),25,s);
 //检查是否成功创建
 if (!writer.isOpened())
 {
  cerr << "Can not create video file.\n"<<endl;
  return -1;
 }
 //视频帧
 Mat frame(s,CV_8UC3);
 for (int i = 0; i < 100; i++)
 {
  //将图像置为黑色
  frame = Scalar::all(0);
  //将整数i转为i字符串类型
  char text[128];
  sprintf_s(text,"%d",i);
  //将数字绘到画面上
  putText(frame,text,Point(s.width/3,s.height/3),FONT_HERSHEY_SCRIPT_SIMPLEX,3,Scalar(0,0,255),3,8);
  //将图像写入视频
  writer << frame;
 }
 VideoCapture cap("myvedio.avi");
 for (int i = 0; i < 100; i++)
 {
  Mat frame;
  //从 cap 中读一帧,存到 frame 
  cap >> frame;
  //如果未读到图像 
  if (frame.empty())
   break;
  //显示结果 
  imshow("frame", frame);
  waitKey(1000);
 }
 //退出程序时自动关闭视频文件
 waitKey(0);
 return 0;
}
View Code

 

 

本课程涉及到深度学习与机器学习相关的内容,所以要学习一下python

 python环境的安装,建议Anaconda+Ipython+(PyScripter)(版本2.7比较流行或者3.5都可以)

print函数在2.7没有括号,在3.5有括号

在2.7版本

x=3,x/2=1,是整型

在3.5版本

x=3,x/2=1.5是浮点型

机器学习

机器学习:要随机得观测数据,要随机得对观测数据进行采样,要学习数据的性质,其实也叫做特征feature,以及属性,属性通常叫做label,他是属于那一个类别,再来预测新的未知的数据的属性。在机器学习当中,我们通常要把数据分成两部分,一部分叫做training(训练集用来学习数据特征),另一部分叫做test(测试集用来测试算法是否准确)。机器学习的问题主要分为

监督学习和非监督学习

监督学习除了数据本身以外,还有额外的属性部分,也就是说会有一些feature,然后再额外的会有一些label,这样的问题分为分类和回归两部分;分类问题可以理解为输出是离散数据;回归问题输出的就是连续的,比如说根据年龄、体重,来预测身高。监督学习主要是分类,尽量把问题转化为分类问题。

如果输入没有额外的label等,那么就叫做非监督学习,主要分为聚类(kmeans)和核密度估计。

 

 

Knn-Grassroots Democracy

当有一个未知物体的时候,在我们做出推荐之前会考虑一下它周围的数据的label是属于那个类别,从而觉得这个数据是属于那个类别的。

KNN算法应用得背景是字符识别,最常见的数据库在网站http://yann.lecun.com/exdb/mnist

 

knn作为一种机器学习算法也分为两部分,训练算法识别特征的类别,然后在训练集上测试;KNN的训练部分就是把训练集和标签全部存储,比如说,给出5这样一个训练的图像

它的label就是5。会有一个其他图片比如说是0,它的label就是0,

KNN就会把这两个属性全都存储下来,它就学习完了,只要把数据全部喂给算法,它就已经完成识别了,什么操作都没有,接下来就是测试部分,只要提供测试集,没有标签,在指定K的大小之后,让算法来猜它是什么数据。它是没有训练的,平时不学习,等到测试的时候问题就来了,它就把所有的训练集跟测试数据进行一一比对,比对就涉及到一个距离问题,测试两个向量的距离distance。

下面将一下聚类算法

上面图在轮廓上是比较相似的,但是在语义上是不同的。

讲到聚类算法必须提到K-means算法

 

posted on 2017-01-11 19:00  gary_123  阅读(454)  评论(0编辑  收藏  举报

导航