目录

一、opencv的前身后世

1、简介

2、IplImage介绍

3、Mat介绍

二、哈哈镜介绍

1、原理

2、实现

3、凸透镜算法

4、凹透镜算法


因为要做一个项目,为了实现他的趣味性,所以想应用图像处理做一些东西,在上次完成卡通化之后,又了解了哈哈镜效果,想自己实现,从网上找了好多教程,都是以前的opencv版本的代码,在opencv3.0及以上版本已经不支持使用了。

可能最新学习opencv的小伙伴不了解什么是“以前的opencv版本”。所以我先简单介绍一下。

一、opencv的前身后世

1、简介

OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

OpenCV用C++语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言接口。所有新的开发和算法都是用C++接口。一个使用CUDA的GPU接口也于2010年9月开始实现。

其他的一些介绍就在这里不多说了,大家在网上也能找到。我主要再说一下在前面我说到的以前的opencv版本和新版本的差别。这个差别不是opencv2.0,opencv2.3.4,opencv3.0.0,opencv3.1.0,opencv3.4.0等等这些版本之间的差别。大家会发现,大家现在在学习opencv时,建立图像,用的时C++语言中的Mat类,最初的opencv是用C语言编写的,C语言是没有类的,那用C语言用的自然就是结构体。所以接下来我讲一下opencv结构体的表示。

2、IplImage介绍

在OpenCV中IplImage是表示一个图像的结构体,也是从OpenCV1.0到目前最为重要的一个结构;在之前的图像表示用IplImage,而且之前的OpenCV是用C语言编写的,提供的接口也是C语言接口;

英文注释版结构体如下:

typedef struct _IplImage
{int  nSize;             /* sizeof(IplImage) */int  ID;                /* version (=0)*/int  nChannels;         /* Most of OpenCV functions support 1,2,3 or 4 channels */int  alphaChannel;      /* Ignored by OpenCV */int  depth;             /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported.  */char colorModel[4];     /* Ignored by OpenCV */char channelSeq[4];     /* ditto */int  dataOrder;         /* 0 - interleaved color channels, 1 - separate color channels.cvCreateImage can only create interleaved images */int  origin;            /* 0 - top-left origin,1 - bottom-left origin (Windows bitmaps style).  */int  align;             /* Alignment of image rows (4 or 8).OpenCV ignores it and uses widthStep instead.    */int  width;             /* Image width in pixels.                           */int  height;            /* Image height in pixels.                          */struct _IplROI *roi;    /* Image ROI. If NULL, the whole image is selected. */struct _IplImage *maskROI;      /* Must be NULL. */void  *imageId;                 /* "           " */struct _IplTileInfo *tileInfo;  /* "           " */int  imageSize;         /* Image data size in bytes(==image->height*image->widthStepin case of interleaved data)*/char *imageData;        /* Pointer to aligned image data.         */int  widthStep;         /* Size of aligned image row in bytes.    */int  BorderMode[4];     /* Ignored by OpenCV.                     */int  BorderConst[4];    /* Ditto.                                 */char *imageDataOrigin;  /* Pointer to very origin of image data(not necessarily aligned) -needed for correct deallocation */
}
IplImage;

中文注释版结构体如下:

typedef struct _IplImage{int  nSize;         /* IplImage大小 */int  ID;            /* 版本 (=0)*/int  nChannels;     /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */int  alphaChannel;  /* 被OpenCV忽略 */int  depth;         /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */char colorModel[4]; /* 被OpenCV忽略 */char channelSeq[4]; /* 同上 */int  dataOrder;     /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道.cvCreateImage只能创建交叉存取图像 */int  origin;        /* 0 - 顶—左结构,1 - 底—左结构 (Windows bitmaps 风格) */int  align;         /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */int  width;         /* 图像宽像素数 */int  height;        /* 图像高像素数*/struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */void  *imageId;     /* 同上*/struct _IplTileInfo *tileInfo; /*同上*/int  imageSize;     /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/char *imageData;  /* 指向排列的图像数据 */int  widthStep;   /* 排列的图像行大小,以字节为单位 */int  BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */int  BorderConst[4]; /* 同上 */char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */}IplImage;

IplImage结构体是整个OpenCV函数库的基础,在定义该结构变量时需要用到函数cvCreatImage,变量定义方法如下:

//定义一个IplImage指针变量src,图像的大小是200×300,图像颜色深度8位,3通道图像。
IplImage* src = "/cvCreateImage"(cvSize(200, 300), IPL_DEPTH_8U, 3);//定义一个IplImage指针变量src,图像的大小是200×300,图像颜色深度8位,单通道图像。
IplImage* src = "/cvCreateImage"(cvSize(200, 300), IPL_DEPTH_8U, 1);

由于定义的src是一个指针变量,所以通过src来调用函数时,采用的是指向的方式:

//下面是两种图像数据存取方式的例子://1.直接存取 : (效率高, 但容易出错)
//  对单通道字节图像 :
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1);
((uchar *)(src->imageData + i*src->widthStep))[j] = 111;//  对多通道字节图像:
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3);
((uchar *)(src->imageData + i*src->widthStep))[j*src->nChannels + 0] = 111; // B
((uchar *)(src->imageData + i*src->widthStep))[j*src->nChannels + 1] = 112; // G
((uchar *)(src->imageData + i*src->widthStep))[j*src->nChannels + 2] = 113; // R//  对多通道浮点图像:
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_32F, 3);
((float *)(src->imageData + i*src->widthStep))[j*src->nChannels + 0] = 111; // B
((float *)(src->imageData + i*src->widthStep))[j*src->nChannels + 1] = 112; // G
((float *)(src->imageData + i*src->widthStep))[j*src->nChannels + 2] = 113; // R//2.用指针直接存取 : (在某些情况下简单高效)
//  对单通道字节图像 :
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1);
int height = src->height;
int width = src->width;
int step = src->widthStep / sizeof(uchar);
uchar* data = (uchar *)src->imageData;
data[i*step + j] = 111;//  对多通道字节图像:
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3);
int height = src->height;
int width = src->width;
int step = src->widthStep / sizeof(uchar);
int channels = src->nChannels;
uchar* data = (uchar *)src->imageData;
data[i*step + j*channels + k] = 111;//  对多通道浮点图像(假设用4字节调整) :
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_32F, 3);
int height = src->height;
int width = src->width;
int step = src->widthStep / sizeof(float);
int channels = src->nChannels;
float * data = (float *)src->imageData;
data[i*step + j*channels + k] = 111;

3、Mat介绍

具体介绍内容详解我的:【opencv学习笔记】004之Mat对象 。里面有Mat简介,常用成员,构造函数以及三种图像类型格式的转换。

二、哈哈镜介绍

1、原理

哈哈镜是表面凸凹不平的镜面,反映人像及物件的扭曲面貌,令人发笑,故名叫哈哈镜。哈哈镜的原理是曲面镜引起的不规则光线反射与聚焦,做成散乱的影像。镜面扭曲的情况不同,成像的效果也会相异。 常见的变换效果有高矮胖瘦四种效果,镜面材质有金属哈哈镜,玻璃哈哈镜等。

对应到物理中,哈哈镜其实是光的折射,可以理解为数学中的映射,不同的映射会有不同的效果,如线性映射会产生放大缩小的感觉,凸函数则会是凸透镜,凹函数就是凹透镜,原则上,不同的函数就不会产生不同的结果。

所以如果希望通过opencv来做哈哈镜,就需要找到一个对应的映射,让图像的像素扭曲,从而实现哈哈效果。在这里,实现了放大镜和缩小镜。

2、实现

我希望实现的是实时将视频图像卡通化,所以需要通过opencv调用摄像头,并对其进行一系列设置。在这里,采用了最简单的调用摄像头的方式:

VideoCapture capture;
capture.open(0);

获取到每一帧的图像后,需要对图像做一定的处理,因为用了两种方式做处理分别得到:放大镜,缩小镜。所以在处理之前加一个整形变量,允许用户输入,自由选择处理方式,为了防止用户非法输入,我设置循环做判断。输入合法后才允许执行下面的代码。并通过Switch语句设置两种处理方式。代码如下:

        int mode = -1;//动画处理模式cout << "请输入类型:";cin >> mode;while (mode<0 || mode >= 2){cout << "处理模式输入错误,请重新输入:";cin >> mode;}switch (mode){case 0:while (1){capture >> hahaFrame;hahaFrame.copyTo(img);magnifyGlass(hahaFrame,img);imshow("【放大镜】", img);waitKey(30);}break;case 1:while (1){capture >> hahaFrame;hahaFrame.copyTo(img);compressGlass(hahaFrame,img);imshow("【压缩镜】", img);waitKey(30);}break;default:break;}

接下来就是最核心的算法,即映射了。

在前面我们说到,所谓哈哈镜,就是图像像素点位置的变化,所以我们要获取到每个像素点的像素值,然后对像素点做操作。

3、凸透镜算法

void magnifyGlass(Mat hahaFrame,Mat img) {//【1】凸透镜int width = hahaFrame.cols;int heigh = hahaFrame.rows;Point center(width / 2, heigh / 2);int R = sqrtf(width*width + heigh*heigh) / 2; //直接关系到放大的力度,与R成正比;  for (int y = 0; y < heigh; y++){uchar *img_p = img.ptr<uchar>(y);//定义一个指针,指向第y列,从而可以访问行数据。for (int x = 0; x < width; x++){int dis = norm(Point(x, y) - center);//获得当前点到中心点的距离if (dis < R)//设置变化区间{int newX = (x - center.x)*dis / R + center.x;int newY = (y - center.y)*dis / R + center.y;img_p[3 * x] = hahaFrame.at<uchar>(newY, newX * 3);img_p[3 * x + 1] = hahaFrame.at<uchar>(newY, newX * 3 + 1);img_p[3 * x + 2] = hahaFrame.at<uchar>(newY, newX * 3 + 2);}}}
}

4、凹透镜算法

void compressGlass(Mat hahaFrame,Mat img) {//【2】凹透镜int width = hahaFrame.cols;int heigh = hahaFrame.rows;Point center(width / 2, heigh / 2);for (int y = 0; y<heigh; y++){uchar *img_p = img.ptr<uchar>(y);for (int x = 0; x<width; x++){double theta = atan2((double)(y - center.y), (double)(x - center.x)); int R = sqrtf(norm(Point(x, y) - center)) * 8; //直接关系到挤压的力度,与R成反比;  int newX = center.x + (int)(R*cos(theta));int newY = center.y + (int)(R*sin(theta));if (newX<0) newX = 0;else if (newX >= width) newX = width - 1;if (newY<0) newY = 0;else if (newY >= heigh) newY = heigh - 1;img_p[3 * x] = hahaFrame.at<uchar>(newY, newX * 3);img_p[3 * x + 1] = hahaFrame.at<uchar>(newY, newX * 3 + 1);img_p[3 * x + 2] = hahaFrame.at<uchar>(newY, newX * 3 + 2);}}
}

【opencv实战】哈哈镜相关推荐

  1. 再次升级,985博士整理的71个OpenCV实战项目教程开放下载!

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的 ...

  2. 重磅升级,52个Python+OpenCV实战项目教你掌握图像处理

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的 ...

  3. 基于OpenCV实战:3步实现图像降噪

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 在本文中,我们将展示如何通过三个简单的步骤来实现降噪.我们将使用机 ...

  4. 基于OpenCV实战:绘制图像轮廓(附代码)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 山区和地形图中海拔高的区域划出的线称为地形轮廓,它们提供了地形的高 ...

  5. 基于OpenCV实战:车牌检测

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 拥有思维导图或流程将引导我们朝着探索和寻找实现目标的正确道路的方向 ...

  6. 基于OpenCV实战的图像处理:色度分割

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 通过HSV色阶使用彩色图像可以分割来分割图像中的对象,但这并不是分 ...

  7. 基于OpenCV实战:提取中心线

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|AI算法与图像处理 问题 前几天有个人问了我一个问题,问 ...

  8. 基于OpenCV实战:对象跟踪

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 介绍 跟踪对象的基本思想是找到对象的轮廓,基于HSV颜色值. 轮廓 ...

  9. Opencv实战 | 用摄像头自动化跟踪特定颜色物体

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自:新机器视觉 1. 导语 在之前的某个教程里,我们探讨了如 ...

  10. 基于opencv实战眼睛控制鼠标

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 如何用眼睛来控制鼠标?一种基于单一前向视角的机器学习眼睛姿态估计方 ...

最新文章

  1. 加拿大文件服务器,加拿大服务器空间
  2. Java中的方法调用有多昂贵
  3. 【直播回顾】阿里高级开发工程师紫思:闲鱼多业务隔离框架SWAK...
  4. 设计模式总结(Java)—— 单例模式
  5. 熵、条件熵、互信息等概念
  6. 基于PHP+MySQL的物流配送管理系统平台
  7. windows下 C++ openCV配置及x86编译(傻瓜式教程)
  8. 给ftp服务器创建文件夹,ftp服务器上创建文件夹
  9. linux mbr efi 分区吗,几个概念:MBR,GPT,EFI分区,混合分区表,BootCamp
  10. RTC领域首个AI算法大赛 AI in RTC 2019 创新挑战圆满落幕
  11. 试题 算法训练 后缀数组——最长重复子串
  12. 三维重建中经常遇到的拓扑学概念的通俗解释
  13. linux 下跑通pointnet++网络模型
  14. 【博客496】k8s dns解析与服务发现原理
  15. 计算机课题参与者的学术背景,课题参与有几种方法
  16. 十分钟看懂图像语义分割技术
  17. Juniper SRX Junos升级
  18. 2018-2019(1)教学随笔
  19. 2016考试计算机知识基础题库,计算机考试题库:计算机基础练习题(58)
  20. 计算机物联网专业排名,物联网专业排名

热门文章

  1. 树冠体积计算之体元累加法
  2. hilink互联技术_hilink是什么意思
  3. 利用hilink 插座远程开电脑
  4. Python编译成.so文件后调用
  5. 自学python还是报班-自学python还是报班学?老男孩培训Python开发
  6. 文件和目录的常用命令
  7. 关于Typecho的主题魔改记录
  8. java设计一个排队叫号系统_一个简易的叫号系统实现方案
  9. supervisor使用
  10. 实现SP端的协议开发