前言
????本篇文檔的demo包含了 1.使用OpenCV對圖像進(jìn)行處理,對圖像進(jìn)行置灰,旋轉(zhuǎn),摳圖,高斯模糊,中值濾波,部分區(qū)域清除置黑,背景移除,邊緣檢測等操作;2.單純使用opencv播放顯示視頻;3.使用opencv和openGL播放顯示視頻;4.在ffmpeg解碼后,使用opencv顯示視頻,并支持對視頻的旋轉(zhuǎn)翻轉(zhuǎn)、裁剪、添加文字、添加logo、亮度調(diào)節(jié)、置灰、錄像截圖,音頻開關(guān)等功能。視頻播放器同時(shí)支持本地文件與網(wǎng)絡(luò)碼流地址的播放。本篇博客的最后有提供工程代碼的下載。
一、OpenCV簡單介紹:
????OpenCV是一個(gè)基于Apache2.0許可(開源)發(fā)行的跨平臺計(jì)算機(jī)視覺和機(jī)器學(xué)習(xí)軟件庫,可以運(yùn)行在Linux、Windows、Android和Mac OS操作系統(tǒng)上。它由一系列 C 函數(shù)和少量 C++ 類構(gòu)成,同時(shí)提供了Python、Ruby、MATLAB等語言的接口,實(shí)現(xiàn)了圖像處理和計(jì)算機(jī)視覺方面的很多通用算法。應(yīng)用領(lǐng)域包含:人機(jī)互動(dòng)、物體識別、圖像分割、人臉識別、動(dòng)作識別、運(yùn)動(dòng)跟蹤、機(jī)器人、運(yùn)動(dòng)分析、機(jī)器視覺、結(jié)構(gòu)分析、汽車安全駕駛等。
二、demo調(diào)用OpenCV:
????本篇demo使用的是OpenCV-4.5.1, 是直接下載windows版本,沒有額外去編譯OpenCV的庫,安裝exe后,提取頭文件和相關(guān)的庫文件,用于工程調(diào)用即可。OpenCV下載路徑:https://sourceforge.net/projects/opencvlibrary/
????OpenCV常用函數(shù)介紹:
????1)Mat Image = imread(filename); //從指定文件加載圖像并返回
????2)namedWindow(“IMG-WIN”, cv::WINDOW_NORMAL);//創(chuàng)建圖像顯示窗口
????3)destroyWindow(“IMG-WIN”);//銷毀圖像顯示窗口
????4)imshow(“IMG-WIN”, Image);//將圖片顯示在窗口中
????5)imwrite將圖像保存到指定文件
????6)其他
三、demo功能介紹:
3.1.圖像操作
????圖像操作包含了顯示原圖、置灰、旋轉(zhuǎn)、摳圖、高斯模糊、中值濾波,部分區(qū)域清除置黑,背景移除,邊緣檢測等操作。點(diǎn)擊顯示原圖后,通過點(diǎn)擊其他按鈕,可以實(shí)現(xiàn)圖像的不同效果,例如置灰:
????其他功能可下載demo進(jìn)行操作,點(diǎn)擊顯示原圖,可恢復(fù)到之前圖片顯示,點(diǎn)擊保存圖片按鈕可將處理后的圖片進(jìn)行保存。
3.2.圖像操作相關(guān)代碼
//圖像操作
/
void MainWindow::on_pushButton_showImage_clicked()
{
// load image using opencv
m_srcImage = imread("./Image/test1.jpg");
std::string winName = "Display window";
namedWindow(winName, WINDOW_AUTOSIZE);
imshow(winName, m_srcImage);
if(m_bShowImage == false)
{
HWND hwnd = static_cast<HWND>(cvGetWindowHandle(winName.c_str()));
HWND parent = GetParent(hwnd);//得到nameWindow窗口的父句柄
SetParent(hwnd, (HWND)ui->widget_image->winId());//設(shè)置ui控件的句柄是父句柄
ShowWindow(parent, SW_HIDE);//隱藏掉nameWindow窗口
resizeWindow(winName, cv::Size(ui->widget_image->width(), ui->widget_image->height()));
}
m_destImage = m_srcImage;
m_bShowImage = true;
}
void MainWindow::on_pushButton_saveImage_clicked()
{
if(m_bShowImage)
{
std::string saveFile = "./Image/test-new.jpg";
if(imwrite(saveFile, m_destImage))
{
QMessageBox::information(this, "myOpencv", "Save Image Success.");
}
else
{
QMessageBox::critical(this, "myOpencv", "Save Image Failed.");
}
}
else
QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}
void MainWindow::on_pushButton_gray_clicked()
{
if(m_bShowImage)
{
cvtColor(m_srcImage, m_destImage, COLOR_BGR2GRAY);
std::string winName = "Display window";
namedWindow(winName, WINDOW_AUTOSIZE);
imshow(winName, m_destImage);
}
else
QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}
void MainWindow::on_pushButton_rotate_clicked()
{
if(m_bShowImage)
{
warpAffine(m_srcImage, m_destImage, getRotationMatrix2D({m_srcImage.cols/2.0f, m_srcImage.rows/2.0f},
45,0.5),m_srcImage.size(),cv::INTER_LINEAR, cv::BORDER_CONSTANT, {255,0,0});
std::string winName = "Display window";
namedWindow(winName, WINDOW_AUTOSIZE);
imshow(winName, m_destImage);
}
else
QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}
void MainWindow::on_pushButton_matting_clicked()
{
if(m_bShowImage)
{
m_destImage = m_srcImage({20, 20, 60, 60}).clone();
std::string winName = "Display window";
namedWindow(winName, WINDOW_AUTOSIZE);
imshow(winName, m_destImage);
}
else
QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}
void MainWindow::on_pushButton_gauss_clicked()
{
if(m_bShowImage)
{
GaussianBlur(m_srcImage, m_destImage, cv::Size(5,5), 0);
std::string winName = "Display window";
namedWindow(winName, WINDOW_AUTOSIZE);
imshow(winName, m_destImage);
}
else
QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}
void MainWindow::on_pushButton_median_clicked()
{
if(m_bShowImage)
{
medianBlur(m_srcImage, m_destImage, 5);
std::string winName = "Display window";
namedWindow(winName, WINDOW_AUTOSIZE);
imshow(winName, m_destImage);
}
else
QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}
void MainWindow::on_pushButton_clear_clicked()
{
if(m_bShowImage)
{
cv::Rect roi(40, 40, 80, 80);
Mat roiMat = m_destImage(roi);
roiMat.setTo(cv::Scalar(0,0,0));
std::string winName = "Display window";
namedWindow(winName, WINDOW_AUTOSIZE);
imshow(winName, m_destImage);
}
else
QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}
void MainWindow::on_pushButton_clearBackground_clicked()
{
//原圖
Mat image = imread("./Image/test1.jpg");
// 創(chuàng)建一個(gè)掩碼(mask),用于指定哪些區(qū)域是前景,哪些區(qū)域是背景
cv::Mat mask(image.size(), CV_8UC1, cv::Scalar(cv::GC_BGD));
cv::Rect rectangle(50, 50, image.cols - 100, image.rows - 100);
cv::grabCut(image, mask, rectangle, cv::Mat(), cv::Mat(), 5, cv::GC_INIT_WITH_RECT);
// 將掩碼中被標(biāo)記為前景的像素設(shè)為白色,背景設(shè)為黑色
cv::Mat foregroundMask = (mask == cv::GC_PR_FGD) | (mask == cv::GC_FGD);
cv::Mat foregroundImage(image.size(), CV_8UC3, cv::Scalar(0, 0, 0));
image.copyTo(foregroundImage, foregroundMask);
// 顯示結(jié)果圖像
cv::imshow("Foreground", foregroundImage);
cv::waitKey(0);
}
void MainWindow::on_pushButton_edgeDetection_clicked()
{
// 讀取圖片
cv::Mat image = cv::imread("./Image/test1.jpg", cv::IMREAD_GRAYSCALE);
// 邊緣檢測
cv::Mat edges;
cv::Canny(image, edges, 100, 200);
// 顯示結(jié)果
cv::imshow("Edges", edges);
cv::waitKey(0);
}
3.3.視頻操作
????代碼中通過宏定義可以旋轉(zhuǎn)不同的視頻處理方式(默認(rèn)使用ffmpeg解碼后,opencv播放顯示視頻),如下:
#define OPENCV_PLAY_VIDEO 0 //單純使用opencv播放顯示視頻
#define OPENCV_OPENGL_PLAY_VIDEO 0 //使用opencv和openGL播放顯示視頻
#define OPENCV_FFMPEG_PLAY_VIDEO 1 //使用ffmpeg解碼后,opencv顯示視頻
????播放器支持對視頻的旋轉(zhuǎn)翻轉(zhuǎn)、裁剪、添加文字、添加logo、亮度調(diào)節(jié)、置灰、錄像截圖,音頻開關(guān)等功能,同時(shí)支持本地文件與網(wǎng)絡(luò)碼流地址的播放。
????簡單演示如下:
????對原視頻進(jìn)行相關(guān)處理后,可通過點(diǎn)擊錄像截圖按鈕進(jìn)行視頻與圖片的保存。通過開關(guān)音頻按鈕,支持對音頻的播放處理。
????demo工程中有提供測試視頻和測試logo,可以進(jìn)行添加測試。
3.4.視頻操作部分代碼
void MainWindow::on_pushButton_Open_clicked()
{
QString sFileName = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("Select Video File"));
if(sFileName.isEmpty())
{
QMessageBox::critical(this, "myOpencv", QObject::tr("錯(cuò)誤:文件不能為空."));
return;
}
ui->lineEdit_url->setText(sFileName);
}
void MainWindow::on_pushButton_Play_clicked()
{
QString sStreamAddr = ui->lineEdit_url->text();
if(sStreamAddr.isEmpty())
{
QMessageBox::critical(this, "myOpencv", QObject::tr("錯(cuò)誤:碼流地址不能為空."));
return;
}
#if OPENCV_FFMPEG_PLAY_VIDEO
if(nullptr == m_pMediaThread)
{
MY_DEBUG << "new MediaThread";
m_pMediaThread = new MediaThread(ui->openGLWidget);
}
else
{
if(m_pMediaThread->isRunning())
{
QMessageBox::critical(this, "myOpencv", "錯(cuò)誤:請先點(diǎn)擊停止按鈕關(guān)閉視頻.");
return;
}
}
bool bMediaInit = m_pMediaThread->Init(sStreamAddr);
if(bMediaInit)
{
m_pMediaThread->startThread();
}
#elif OPENCV_PLAY_VIDEO
if(m_pOpencvPlayThread1 == nullptr)
{
m_pOpencvPlayThread1 = new COpencvPlayThread1(ui->openGLWidget, sStreamAddr);
}
m_pOpencvPlayThread1->startThread();
#elif OPENCV_OPENGL_PLAY_VIDEO
if(m_pOpencvPlayThread2 == nullptr)
{
m_pOpencvPlayThread2 = new COpencvPlayThread2(ui->openGLWidget, sStreamAddr);
}
m_pOpencvPlayThread2->startThread();
#endif
}
void MainWindow::on_pushButton_Stop_clicked()
{
#if OPENCV_PLAY_VIDEO
if(m_pOpencvPlayThread1)
m_pOpencvPlayThread1->stopThread();
#endif
#if OPENCV_OPENGL_PLAY_VIDEO
if(m_pOpencvPlayThread2)
m_pOpencvPlayThread2->stopThread();
#endif
#if OPENCV_FFMPEG_PLAY_VIDEO
if(m_pMediaThread)
{
disconnect(m_pMediaThread, SIGNAL(sig_emitImage(const QImage&)),
ui->openGLWidget, SLOT(slot_showImage(const QImage&)));
m_pMediaThread->stopThread();
m_pMediaThread->quit();
m_pMediaThread->wait();
m_pMediaThread->DeInit();
}
#endif
}
void MainWindow::on_pushButton_startRecord_clicked()
{
if(m_pMediaThread)
m_pMediaThread->startRecord();
}
void MainWindow::on_pushButton_stopRecord_clicked()
{
if(m_pMediaThread)
{
m_pMediaThread->stopRecord();
QDesktopServices::openUrl(QUrl::fromLocalFile(RECORD_DEFAULT_PATH));
}
}
void MainWindow::on_pushButton_snapshot_clicked()
{
if(m_pMediaThread)
{
m_pMediaThread->Snapshot();
QDesktopServices::openUrl(QUrl::fromLocalFile(SNAPSHOT_DEFAULT_PATH));
}
}
void MainWindow::on_pushButton_video_rotate_clicked()
{
if(m_pMediaThread)
{
int nRotate = ui->comboBox_rotate->currentIndex() - 1;
if(nRotate < 0)
m_pMediaThread->setRotate(false, nRotate);
else
m_pMediaThread->setRotate(true, nRotate);
}
}
void MainWindow::on_pushButton_video_overturn_clicked()
{
if(m_pMediaThread)
{
int nOverturn = ui->comboBox_overturn->currentIndex() - 1;
if(nOverturn < 0)
m_pMediaThread->setFlip(false, nOverturn);
else
m_pMediaThread->setFlip(true, nOverturn);
}
}
bool bGrey = false;
void MainWindow::on_pushButton_video_grey_clicked()
{
if(m_pMediaThread)
{
if(bGrey)
{
m_pMediaThread->setGray(false);
bGrey = false;
}
else
{
m_pMediaThread->setGray(true);
bGrey = true;
}
}
}
void MainWindow::on_pushButton_video_cropping_clicked()
{
if(m_pMediaThread)
{
int x = ui->lineEdit_crop_X->text().toInt();
int y = ui->lineEdit_crop_Y->text().toInt();
int w = ui->lineEdit_crop_W->text().toInt();
int h = ui->lineEdit_crop_H->text().toInt();
m_pMediaThread->setCrop(true, x, y, w, h);
}
}
void MainWindow::on_pushButton_video_addText_clicked()
{
if(m_pMediaThread)
{
int x = ui->lineEdit_text_X->text().toInt();
int y = ui->lineEdit_text_Y->text().toInt();
int nColor = ui->comboBox_text_rgb->currentIndex();
double size = ui->comboBox_text_fonsize->currentText().toDouble();
QString sText = ui->lineEdit_videoText->text();
MY_DEBUG << "size:" << size;
m_pMediaThread->setAddText(true, sText, x, y, nColor, size);
}
}
void MainWindow::on_pushButton_logoPath_clicked()
{
QString sFileName = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("Select Logo File"));
if(sFileName.isEmpty())
{
QMessageBox::critical(this, "myOpenCV", QObject::tr("錯(cuò)誤:文件不能為空."));
return;
}
ui->lineEdit_logo_path->setText(sFileName);
}
void MainWindow::on_pushButton_addLogo_clicked()
{
if(m_pMediaThread)
{
int x = ui->lineEdit_logo_X->text().toInt();
int y = ui->lineEdit_logo_Y->text().toInt();
QString sAddLogoPath = ui->lineEdit_logo_path->text();
m_pMediaThread->setAddLogo(true, x, y, sAddLogoPath);
}
}
void MainWindow::on_pushButton_setBrightness_clicked()
{
if(m_pMediaThread)
{
double brightness = ui->comboBox_brightnessVal->currentText().toDouble();
m_pMediaThread->setImagePara(brightness);
}
}
void MainWindow::on_pushButton_restore_clicked()
{
if(m_pMediaThread)
{
int x = ui->lineEdit_crop_X->text().toInt();
int y = ui->lineEdit_crop_Y->text().toInt();
int w = ui->lineEdit_crop_W->text().toInt();
int h = ui->lineEdit_crop_H->text().toInt();
m_pMediaThread->setCrop(false, x, y, w, h);
}
}
void MainWindow::on_pushButto_video_deleteText_clicked()
{
if(m_pMediaThread)
{
int x = ui->lineEdit_text_X->text().toInt();
int y = ui->lineEdit_text_Y->text().toInt();
int nColor = ui->comboBox_text_rgb->currentIndex();
double size = ui->comboBox_text_rgb->currentText().toDouble();
QString sText = ui->lineEdit_videoText->text();
m_pMediaThread->setAddText(false, sText, x, y, nColor, size);
}
}
void MainWindow::on_pushButton_deleteLogo_clicked()
{
if(m_pMediaThread)
{
int x = ui->lineEdit_logo_X->text().toInt();
int y = ui->lineEdit_logo_Y->text().toInt();
QString sAddLogoPath = ui->lineEdit_logo_path->text();
m_pMediaThread->setAddLogo(false, x, y, sAddLogoPath);
}
}
void MainWindow::on_pushButton_audioOpen_clicked()
{
ctAudioPlayer::getInstance().isPlay(true);
}
void MainWindow::on_pushButton_audioClose_clicked()
{
ctAudioPlayer::getInstance().isPlay(false);
}
void ctFFmpeg::dealWithOpenCV(AVFrame *pFrame/*, int time*/)
{
if(!m_bQuit)
{
cv::Mat destCvMat(cv::Size(m_nVideoW, m_nVideoH), CV_8UC3, cv::Scalar(1, 2, 3));
destCvMat.data = (uchar*)pFrame->data[0];
//旋轉(zhuǎn)
if(m_bRotate)
{
MY_DEBUG << "Set Rotate m_nRotate:" << m_nRotate;
rotate(destCvMat, destCvMat, m_nRotate);
}
//翻轉(zhuǎn)
if(m_bFlip)
{
MY_DEBUG << "Set Flip";
flip(destCvMat, destCvMat, m_nFlip);
}
//裁剪
if(m_bCrop)
{
destCvMat = destCvMat(Rect(m_nCropX, m_nCropY, m_nCropW, m_nCropH));
}
//灰度
if(m_bGray)
{
MY_DEBUG << "Set Gray";
cvtColor(destCvMat, destCvMat, COLOR_BGR2GRAY);
}
//添加文字
if(m_bAddText)
{
MY_DEBUG << "m_nAddTextX:" << m_nAddTextX << ", m_nAddTextY:" << m_nAddTextY << ",m_nAddTextSize:" << m_nAddTextSize;
cv::Point ptPos(m_nAddTextX, m_nAddTextY);
int fontFace = cv::FONT_HERSHEY_COMPLEX;
double fontScale = m_nAddTextSize;
int thickness = 2;
cv::Scalar color;
switch(m_nAddTextColor)
{
case ADD_TEXT_COLOR_TYPE_BLUE:
color[0]=255; color[1]=0; color[2]=0;
cv::putText(destCvMat, m_sAddText.toStdString(), ptPos, fontFace, fontScale, color, thickness);
break;
case ADD_TEXT_COLOR_TYPE_GREEN:
color[0]=0; color[1]=255; color[2]=0;
cv::putText(destCvMat, m_sAddText.toStdString(), ptPos, fontFace, fontScale, color, thickness);
break;
case ADD_TEXT_COLOR_TYPE_RED:
default:
color[0]=0; color[1]=0; color[2]=255;//紅色
cv::putText(destCvMat, m_sAddText.toStdString(), ptPos, fontFace, fontScale, color, thickness);
break;
}
}
//添加logo
if(m_bAddLogo)
{
cv::Mat logoImage = cv::imread(m_sAddLogoPath.toStdString());
cv::Mat roi = destCvMat(cv::Rect(m_nAddLogoX, m_nAddLogoY, logoImage.cols, logoImage.rows));
cv::addWeighted(roi, 0.5, logoImage, 0.5, 0, roi);
}
//亮度\暗度
cv::addWeighted(destCvMat, m_nBrightness, cv::Scalar(0, 0, 0), 0, 0, destCvMat);
#if OPENCV_NAME_WINDOW_SHOW
cv::imshow(m_sWinName.toStdString(), destCvMat);
#endif
//效率太慢, 容易導(dǎo)致崩潰, 不適合轉(zhuǎn)QImage再用openGL播放
//...
//emit sig_getImage(image);
//截圖
if(m_bSnapshot)
{
cv::cvtColor(destCvMat, destCvMat, cv::COLOR_BGR2RGB);
QImage image(destCvMat.data, destCvMat.cols, destCvMat.rows, static_cast<int>(destCvMat.step), QImage::Format_RGB888);
QPixmap pixPicture = QPixmap::fromImage(image);
QString sPath = "./snapshot/";
QDate date = QDate::currentDate();
QTime time = QTime::currentTime();
m_sSnapPath = QString("%1%2-%3-%4-%5%6%7.jpg").arg(sPath).arg(date.year()). \
arg(date.month()).arg(date.day()).arg(time.hour()).arg(time.minute()). \
arg(time.second());
MY_DEBUG << "Snapshot... m_sSnapPath:" << m_sSnapPath;
pixPicture.save(m_sSnapPath, "jpg");
m_bSnapshot = false;
}
}
}
四、工程源碼下載:
demo播放器下載:https://download.csdn.net/download/linyibin_123/88217460文章來源:http://www.zghlxwxcb.cn/news/detail-655939.html
demo播放器工程源碼下載:https://download.csdn.net/download/linyibin_123/88217472文章來源地址http://www.zghlxwxcb.cn/news/detail-655939.html
到了這里,關(guān)于opencv+ffmpeg+QOpenGLWidget開發(fā)的音視頻播放器demo的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!