-
作业要求
对输入的一个彩色视频与五张以上照片,用OpenCV实现以下功能或要求:
1. 命令行格式: " xxx.exe 放视频与照片的文件夹路径" ,(例如MyMakeVideo.exe C:\input )【 假设该文件夹下面只有一个avi视频文件与若干jpg文件】
2. 将输入的视频与照片处理成同样长宽后,合在一起生成一个视频;
3. 这个新视频中,编程生成一个片头,然后按幻灯片形式播放这些输入照片,最后按视频原来速度播放输入的视频;
4. 新视频中要在底部打上含自己学号与姓名等信息的字幕;
5. 有能力的同学,可以编程实现镜头切换效果;
- 代码
#include <iostream> #include <string> #include <vector> #include <sstream> #include <opencv2\opencv.hpp> #include <windows.h> #include <math.h> using namespace std; using namespace cv; #define WINDOWN_NAME "Video Output" #define INTERVAL (fps * 2.5)
VideoCapture capture; VideoWriter writer; int width = 1280, height = 728, fps = 25; RNG rng; vector<string> vec;
//放文字 void putTextOnImg(Mat img) { string strHint = "w*d : ", str1, str2, str = "*"; stringstream ss; ss.clear(); ss << width; ss >> str1; ss.clear(); ss << height; ss >> str2; putText(img, strHint + str1 + str + str2, Point(50, 50), CV_FONT_HERSHEY_TRIPLEX, 0.5, Scalar(255, 255, 255), 1, 8, false); strHint = "fps : "; ss.clear(); ss << fps; ss >> str1; putText(img, strHint + str1, Point(50, 70), CV_FONT_HERSHEY_TRIPLEX, 0.5, Scalar(255, 255, 255), 1, 8, false); putText(img, "Number of Effects : 10", Point(50, 90), CV_FONT_HERSHEY_TRIPLEX, 0.5, Scalar(255, 255, 255), 1, 8, false); putText(img, "video_output.avi can be played in player.", Point(50, 110), CV_FONT_HERSHEY_TRIPLEX, 0.5, Scalar(255, 255, 255), 1, 8, false); putText(img, "Press Esc to Exit.", Point(50, 130), CV_FONT_HERSHEY_TRIPLEX, 0.5, Scalar(255, 255, 255), 1, 8, false); putText(img, "11521062 ZhaoLei.", Point(width / 2 - 150, height - 50), FONT_HERSHEY_SCRIPT_COMPLEX, 1, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 2, 8, false); }
//图像切换效果 void effects(int id, Mat img) { Point2f center = Point2f(width / 2, height / 2); Point2f point = Point2f(0, 0); Point2f srcTri[3] = { Point2f(0, 0),Point2f(img.cols, 0),Point2f(0, img.rows) }, dstTri[3]; Point tl, rb; Mat tmpMat, dstImg, blackImg = Mat::zeros(Size(width, height), CV_8UC3); int x, y; cout << "Writing Image"; for (int j = 0; j <= INTERVAL; j++) { switch (id) { case 0: tmpMat = getRotationMatrix2D(center, 0.0, 1.2 / INTERVAL * (INTERVAL - j));//缩小 warpAffine(img, dstImg, tmpMat, img.size()); break; case 1: //仿射变换 x = img.cols / INTERVAL * (INTERVAL - j); y = img.rows / INTERVAL * (INTERVAL - j); dstTri[0] = Point2f(width - x, 0); dstTri[1] = Point2f(x, 0); dstTri[2] = Point2f(width - x, img.rows - y); tmpMat = getAffineTransform(srcTri, dstTri); warpAffine(img, dstImg, tmpMat, img.size()); break; case 2: x = img.cols / INTERVAL * (INTERVAL - j); y = 4 * height * x * (width - x) / (width * width); dstTri[0] = Point2f(width - x, y); dstTri[1] = Point2f(x, y); dstTri[2] = Point2f(x, img.rows - y); tmpMat = getAffineTransform(srcTri, dstTri); warpAffine(img, dstImg, tmpMat, img.size()); tmpMat = getRotationMatrix2D(point, 0, 1.2 / INTERVAL * (INTERVAL - j)); warpAffine(dstImg, dstImg, tmpMat, dstImg.size()); break; case 3: tmpMat = getRotationMatrix2D(point, 0, 1.2 / INTERVAL * j); warpAffine(img, dstImg, tmpMat, img.size()); break; case 4: tmpMat = getRotationMatrix2D(center, 360 / INTERVAL * j * 2, 1.2 / INTERVAL * j);//旋转加放大 warpAffine(img, dstImg, tmpMat, img.size()); break; case 5: addWeighted(img, 1.0 / INTERVAL * (INTERVAL - j), blackImg, 1.0 / INTERVAL * j, 0, dstImg);//图像混合 break; case 6: tl = Point((width / 2) / INTERVAL * (INTERVAL - j), (height / 2) / INTERVAL * (INTERVAL - j)); rb = Point((width / 2) / INTERVAL * j + (width / 2), (height / 2) / INTERVAL * j + (height / 2)); tmpMat = img(Rect(tl.x, tl.y, rb.x - tl.x, rb.y - tl.y));//获取ROI区域 //ROI区域填充矩形 rectangle(tmpMat, Point(0, 0), Point(tmpMat.cols, tmpMat.rows), Scalar(0, 0, 0), CV_FILLED); dstImg = img; break; case 7: tl = Point((width / 2) / INTERVAL * (INTERVAL - j), (height / 2) / INTERVAL * (INTERVAL - j)); rb = Point((width / 2) / INTERVAL * j + (width / 2), (height / 2) / INTERVAL * j + (height / 2)); tmpMat = img(Rect(tl.x, tl.y, rb.x - tl.x, rb.y - tl.y)); circle(tmpMat, Point(tmpMat.cols / 2, tmpMat.rows / 2), tmpMat.cols / 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); dstImg = img; break; case 8: tmpMat = getRotationMatrix2D(point, 0, 1.2 / INTERVAL * (INTERVAL - j)); warpAffine(img, dstImg, tmpMat, img.size()); break; case 9: tmpMat = getRotationMatrix2D(center, 0.0, 1.2 / INTERVAL * j);//放大 warpAffine(img, dstImg, tmpMat, img.size()); break; } putTextOnImg(dstImg); writer.write(dstImg); cout << "."; } cout << "\n"; }
//绘制片头(八卦) void drawStarter() { Mat dstImg = Mat::zeros(Size(width, height), CV_8UC3); int r = height / 2 - 80, a = width / 2, b = height / 2, x, y, ya, yb; int b1 = height / 2 - r / 2, b2 = height / 2 + r / 2; cout << "Writing Starter"; for (int j = 0; j <= INTERVAL * 4; j++) { //绘大圆 x = 2 * r / (INTERVAL * 2) * j + 80; ya = sqrt(pow(r, 2) - pow(x - a, 2)) + b; yb = -sqrt(pow(r, 2) - pow(x - a, 2)) + b; circle(dstImg, Point(x, ya), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); circle(dstImg, Point(x, yb), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); //绘半圆上 y = height / 2 - r / (INTERVAL * 3) * j; x = -sqrt(pow(r / 2, 2) - pow(y - b1, 2)) + a; circle(dstImg, Point(x, y), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); //绘半圆下 y = r / (INTERVAL * 3) * j + height / 2; x = sqrt(pow(r / 2, 2) - pow(y - b2, 2)) + a; circle(dstImg, Point(x, y), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); //绘小圆上 x = r / 4 / (INTERVAL * 3) * j + width / 2 - r / 8; ya = sqrt(pow(r / 8, 2) - pow(x - a, 2)) + b1; yb = -sqrt(pow(r / 8, 2) - pow(x - a, 2)) + b1; circle(dstImg, Point(x, ya), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); circle(dstImg, Point(x, yb), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); //绘小圆下 x = r / 4 / (INTERVAL * 3) * j + width / 2 - r / 8; ya = sqrt(pow(r / 8, 2) - pow(x - a, 2)) + b2; yb = -sqrt(pow(r / 8, 2) - pow(x - a, 2)) + b2; circle(dstImg, Point(x, ya), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); circle(dstImg, Point(x, yb), 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), CV_FILLED); putTextOnImg(dstImg); writer.write(dstImg); cout << "."; } cout << "\n"; }
//wchar2str string wchar2str(LPCWSTR wstr) { int len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); if (len <= 0) return string(""); char* dst = new char[len]; if (dst == NULL) return string(""); WideCharToMultiByte(CP_ACP, 0, wstr, -1, dst, len, NULL, NULL); dst[len - 1] = 0; string str(dst); delete[] dst; return str; }
//遍历文件夹下的文件 int listFiles(string str) { vec.clear(); WIN32_FIND_DATA findData; HANDLE error; //str2wstr wstring widstr = wstring(str.begin(), str.end()); LPWSTR path = (LPWSTR)widstr.c_str();
error = FindFirstFile(path, &findData); if (error == INVALID_HANDLE_VALUE) { cout << "Failed to Find Files!" << "\n"; return 1; } do { string str = wchar2str(findData.cFileName); vec.push_back(str); //cout << str << "\n"; } while (FindNextFile(error, &findData)); return 0; }
int main(int argc, char *argv[]) { if (argv[1] == NULL) { cout << "Please run the program by command line.\ne.g.(Need to enter in the executable directory):\nhomework1.exe E:\\input" << "\n\n"; system("pause"); return 1; }
bool selfExit = false; Mat frame; string strSplit = "\\", strType = "*.avi", fileName; string path = argv[1] + strSplit, fileOutput = "video_output.avi"; listFiles(path + strType); for each (string file in vec) { if (file != "video_output.avi") fileName = file; } capture.open(path + fileName); if (!capture.isOpened()) { cout << "Failed to Open Video!"; return 1; } width = (int)capture.get(CAP_PROP_FRAME_WIDTH);//1280 height = (int)capture.get(CAP_PROP_FRAME_HEIGHT);//720 cout << "w*h:" << width << "*" << height << "\n"; fps = (int)capture.get(CV_CAP_PROP_FPS);//25fps cout << "fps:" << fps << "\n";
writer.open(path + fileOutput, CV_FOURCC('D', 'I', 'V', 'X'), fps, Size(width, height), true); drawStarter(); int i = 0; strType = "*.jpg"; listFiles(path + strType); for each (string fileName in vec) { cout << path + fileName << "\n"; Mat img = imread(path + fileName); Mat dst = Mat::zeros(Size(width, height), CV_8UC3); resize(img, dst, dst.size()); effects(i++ % 10, dst); } cout << "Writing Video Images"; while (1) { if (capture.read(frame)) { putTextOnImg(frame); writer.write(frame); cout << "."; } else break; } cout << "\n";
namedWindow(WINDOWN_NAME); capture.open(path + fileOutput); while (1) { if (capture.read(frame)) imshow(WINDOWN_NAME, frame); else break; //每帧间隔interval ms,且按esc退出 if (waitKey(1000 / fps) == 27) { selfExit = true; break; } } cout << "All are successful!\n"; if (!selfExit) waitKey(); return 0; } |
-
效果
media文件夹内的内容(其中video_output.avi为最后生成的视频文件):
片头效果截图:
切换效果截图(10种中的一个):
原视频截图: