OpenCV C++案例,蓝线特效及相册查看器
关注涛涛CV,设置星标,更新不错过
作者Zero___Chen
来源blog.csdn.net/Zero___Chen
本文将使用OpenCV C++ 实现抖音上的特效“蓝线挑战”。
虽然看起来觉得很牛的样子,但如果了解其中的原理就非常简单了。本案例是我自己对于这个特效实现过程的理解,仅供参考。
算法原理可以分为三个流程:
1、将视频(图像)从(顶->底)或(左->右)逐行(列)扫描图像。
2、将扫描完成的行(列)像素重新生成定格图像。
3、使用原帧图像像素填充未扫描到的像素。
接下来就具体来看看是如何实现的吧。
一、图像扫描
首先第一步,拿到一个视频(很多帧图像)可以简单的看成图像处理。我们需要将图像从顶到底逐行进行像素扫描,当然也可以从左到右逐列扫描,这要看你想要实现什么样的效果。在这里,我实现的是从上到下逐行扫描。效果如图所示。

二、生成定格图像
所谓生成定格图像就是将我们每扫描到的行像素重新进行绘制。
//从顶向下逐行扫描图像if (h < height){h++;//将扫描到的图像像素进行重新绘制,生成新图像for (int j = 0; j < width; j++){for (int c = 0; c < 3; c++){temp.at(h, j)[c] = canvas.at(h, j)[c];}}//绘制扫描过程line(canvas, Point(0, h), Point(width, h), Scalar(255, 255, 0), 2);}

如图所示,这是使用上面代码段实现的逐行扫描生成定格图像。从效果上看,已经得到了我们想要的大致效果了。不过现在的问题是,经扫描到的行有像素填充,未扫描到的行还是漆黑一片。所以接下来我们需要做的就是将未扫描到的行用原图进行填充。具体请看源码注释。
三、图像混合
//将两幅图像进行线性混合bool Linear_Blend(Mat src1, Mat src2, Mat& dst){/*参数说明:src1:生成的定格图像。由于生成的定格图像是从顶往下逐行扫描,故在扫描线下的像素为0src2:原视频帧图像dst:新生成的定格图像。算法原理:经扫描到的像素用src1进行填充,未扫描到的像素用src2进行填充,这样就可以得到我们所要的效果了。*/for (int i = 0; i < src1.rows; i++){for (int j = 0; j < src1.cols; j++){for (int c = 0; c < 3; c++){if (src1.at(i, j)[0] != 0){dst.at(i, j)[c] = src1.at(i, j)[c];}else{dst.at(i, j)[c] = src2.at(i, j)[c];}}}}return true;}
四、效果显示

如上图所示,至此我们已经完成了案例所想要的效果。请参考源码,注释也比较详细了。
五、源码
using namespace std;using namespace cv;/*抖音特效:蓝线挑战算法原理:1、将视频(图像)从(顶->底)或(左->右)逐行(列)扫描图像。2、将扫描完成的行(列)像素重新生成定格图像3、使用原帧图像像素填充未扫描到的像素*///将两幅图像进行线性混合bool Linear_Blend(Mat src1, Mat src2, Mat& dst){/*参数说明:src1:生成的定格图像。由于生成的定格图像是从顶往下逐行扫描,故在扫描线下的像素为0src2:原视频帧图像dst:新生成的定格图像。算法原理:经扫描到的像素用src1进行填充,未扫描到的像素用src2进行填充,这样就可以得到我们所要的效果了。*/for (int i = 0; i < src1.rows; i++){for (int j = 0; j < src1.cols; j++){for (int c = 0; c < 3; c++){if (src1.at<Vec3b>(i, j)[0] != 0){dst.at<Vec3b>(i, j)[c] = src1.at<Vec3b>(i, j)[c];}else{dst.at<Vec3b>(i, j)[c] = src2.at<Vec3b>(i, j)[c];}}}}return true;}int main(){VideoCapture capture;capture.open("test.avi");if (!capture.isOpened()){cout << "can not open the camera!" << endl;system("pause");return -1;}int width = capture.get(CAP_PROP_FRAME_WIDTH);//视频帧宽int height = capture.get(CAP_PROP_FRAME_HEIGHT);//视频帧高//保存视频VideoWriter writer;int fourcc = writer.fourcc('m', 'p', '4', 'v'); //视频编码Size size(capture.get(CAP_PROP_FRAME_WIDTH), capture.get(CAP_PROP_FRAME_HEIGHT));writer.open("result.avi", fourcc, 30, size, true);int h = 0;//定义变量,代表当前扫描高度//用于生成定格照Mat temp = Mat::zeros(Size(width, height), CV_8UC3);Mat frame;while (capture.read(frame)){//将图像拷贝一份,用于每帧更新Mat canvas = frame.clone();//从顶向下逐行扫描图像if (h < height){h++;//将扫描到的图像像素进行重新绘制,生成新图像for (int j = 0; j < width; j++){for (int c = 0; c < 3; c++){temp.at<Vec3b>(h, j)[c] = canvas.at<Vec3b>(h, j)[c];}}//绘制扫描过程line(canvas, Point(0, h), Point(width, h), Scalar(255, 255, 0), 2);}Mat result = Mat::zeros(frame.size(), frame.type());//蓝线挑战最终定格图Linear_Blend(temp, canvas, result); //将两张图像进行像素叠加//writer.write(temp);//进行视频保存imshow("定格图像", temp);imshow("原视频帧", canvas);imshow("蓝线挑战", result);char key = waitKey(10);if (key == 27) break;}capture.release();system("pause");return 0;}
总结
本文使用OpenCV C++ 实现抖音特效“蓝线挑战”,关键步骤有以下几点。
1、将图像进行逐行扫描
2、将扫描到的像素逐行生成定格图像
3、将定格图像与原图像进行像素叠加。
本文将使用OpenCV C++ 制作电子相册查看器。
类似于win10系统的“照片”功能。接下来就具体来看看是如何一步步的实现吧。
一、图片读取
我们想要一张张的查看文件夹下的图片,第一步就得读取将该文件夹下的所有图片。

如上图所示,为我创建的文件夹,该文件夹下有14张图片。接下来我们就编写代码读取该文件夹下的所有图片。将读取到的图片存储在images容器。
//读取文件夹下所有图片string filename = "images";vectorimageList;glob(filename, imageList);vectorimages;for (int i = 0; i < imageList.size(); i++){Mat img = imread(imageList[i]);images.push_back(img);}
现在我们已经有了images容器,其实再使用一个for循环就能够一张张读取容器里的图片了。不过这样只能一张张往下读取,直到读取完最后一张图片程序结束。本案例的需求是使用键盘按键“->”向后读取,“<-”向前读取。
二、图片展示
我们需要一张白色的画布用来放置图片。为了将所有图片都居中在画布中显示,令画布中心为(cx,cy),当前图片宽width,高height。则该图片相对于画布起点为(x,y)。如下图所示。

//将每一张照片放置画布中心int x = cx - (width / 2);int y = cy - (height / 2);//将照片抠图到画布上,此时照片位于画布中心位置images[index].copyTo(bg(Rect(x, y, width, height)));
在这里,使用一个判断语句,判断当前图片尺寸是否大于画布尺寸。如果当前图片尺寸大于画布尺寸,则将图片自适应剪切。否则的话,会造成内存溢出。
//如果图片过大,则对其进行裁剪if (width > canvas.cols || height > canvas.rows){//进行自适应剪切,每次只在原基础上剪切百分之八十while (true){resize(images[index], images[index], Size(0, 0), 0.8, 0.8, INTER_LINEAR);if (images[index].cols < canvas.cols&&images[index].rows < canvas.rows){break;}}width = images[index].cols;height = images[index].rows;}
三、键盘控制
根据上述代码我们已经可以将图片显示在画布中心了,接下来就需要使用键盘响应事件控制图片查看。
我们使用方向键“->”控制向下查看,“<-”控制向上查看。具体请看源码注释。
if (key == 2424832){//如果按动键盘‘←’键,则向前查看相片if (index > 0)//如果图片不是图库中第一张,则允许向前查看{cout << "←" << endl;index--;}}else if (key == 2555904){//如果按动键盘‘→’键,则向后查看相片if (index < size-1)//如果图片不是图库中最后一张,则允许向后查看{cout << "→" << endl;index++;}}//如果按动键盘‘ESC’键,则退出程序else if (key == 27){break;}
四、效果显示

如上图所示,至此我们已经完成了案例所想要的效果。请参考源码,注释也比较详细了。
五、源码
using namespace std;using namespace cv;int main(){//读取文件夹下所有图片string filename = "images";vector<string>imageList;glob(filename, imageList);vector<Mat>images;for (int i = 0; i < imageList.size(); i++){Mat img = imread(imageList[i]);images.push_back(img);}//创建画布,用于放置相片Mat canvas = Mat(Size(1400, 900), CV_8UC3, Scalar::all(255));//画布中心int cx = canvas.cols / 2;int cy = canvas.rows / 2;int size = images.size();//图库中相片数量int index = 0; //当前图库中相片索引while (true){//waitKey无法正常捕捉方向键(上下左右),故使用waitKeyExint key = waitKeyEx(0);if (key == 2424832){//如果按动键盘‘←’键,则向前查看相片if (index > 0)//如果图片不是图库中第一张,则允许向前查看{cout << "←" << endl;index--;}}else if (key == 2555904){//如果按动键盘‘→’键,则向后查看相片if (index < size-1)//如果图片不是图库中最后一张,则允许向后查看{cout << "→" << endl;index++;}}//如果按动键盘‘ESC’键,则退出程序else if (key == 27){break;}//将画布拷贝一份,每经一次循环,更新一次图片。Mat bg = canvas.clone();//计算每一张图片的宽高int width = images[index].cols;int height = images[index].rows;//如果图片过大,则对其进行裁剪if (width > canvas.cols || height > canvas.rows){//进行自适应剪切,每次只在原基础上剪切百分之八十while (true){resize(images[index], images[index], Size(0, 0), 0.8, 0.8, INTER_LINEAR);if (images[index].cols < canvas.cols&&images[index].rows < canvas.rows){break;}}width = images[index].cols;height = images[index].rows;}//将每一张照片放置画布中心int x = cx - (width / 2);int y = cy - (height / 2);//将照片抠图到画布上,此时照片位于画布中心位置images[index].copyTo(bg(Rect(x, y, width, height)));imshow("Demo", bg);}destroyAllWindows();system("pause");return 0;}
总结
本文使用OpenCV C++ 制作电子相册查看器,类似win10系统下的“照片”功能,关键步骤有以下几点。
1、图片在画布上居中显示
2、使用键盘响应事件控制相片上下读取
总结:
课程:
《机器视觉:应用讲解》,一总体概述,二相机篇,三镜头篇,四光源篇,五光学系统选型,六视觉开发软件,七相机标定技术,八项目案例解析,九视觉公司分析,十产业发展情况
笔记:
《智能革命》《人工智能》《AI•未来》《好好赚钱》《韭菜的自我修养》读书笔记
行业:
服务机器人公司,机器视觉公司,自动驾驶公司,ADAS公司总结, 防疫机器人发展,腾讯未来交通
SLAM:
Vslam方案+源码,语义SLAM与深度相机,SLAM和导航避障,视觉SLAM总结
秦学英《三维物体的识别与跟踪》,章国锋《视觉SLAM》,申抒含《基于图像的三维建模》,姜翰青《RGB -D SLAM》记录笔记
机器视觉:
毫米波雷达,雷达视觉融合,2021视觉研讨会,2020上海研讨会,双目和激光的三维重建,2021视觉市场研究,太阳能行业应用
机器视觉基本概念笔记,记录五,记录四,记录三,记录二,记录一
图像处理:
图像处理基本概念笔记,记录八,记录七,记录六 ,记录五,记录四 ,记录三,记录二 ,记录二,记录一
欢迎支持,点击在看,分享
