首先展示一下效果,这里是原图,二次元图片效果更加。
接下来是实现效果,生成avi文件,我这里为了方便显示使用gif动画。
该实现的核心代码为:
// TODO: 在此添加命令处理程序代码
#ifdef NEED_DIR
CString strDir;
GetDlgItemText(IDC_EDIT_DIR, strDir);
if (!strDir.GetLength())
{
strDir = CMfcStrFile::BrowseDir();
if (strDir.GetLength() > 0)
{
SetDlgItemText(IDC_EDIT_DIR, strDir);
}
else
{
return;
}
}
_tstring sDir = CMfcStrFile::CString2string(strDir);
m_cfg.vDirPaths.clear();
m_cfg.vDirPaths.push_back(sDir);
#endif // NEED_DIR
#ifdef NEED_FILE
CString strFile;
GetDlgItemText(IDC_EDIT_FILE, strFile);
if (!strFile.GetLength())
{
strFile = CMfcStrFile::OpenFile();
if (strFile.GetLength() > 0)
{
SetDlgItemText(IDC_EDIT_FILE, strFile);
}
else
{
return;
}
}
_tstring sFile = CMfcStrFile::CString2string(strFile);
m_cfg.vFilePaths.clear();
m_cfg.vFilePaths.push_back(sFile);
#endif // NEED_FILE
//开始显示进度
CTaskBarProgress tbp(m_hWnd);
CProgressInterface* ppi = &tbp;
ppi->Start();
CElapsedTime et;
//记录日志
CLOG::Out("start task!");
//记录耗时
et.Begin();
/*********************************这里增加主程序 开始***************************************/
ppi->OutputInfo("something write here!");
std::string sFilePath = CStdStr::ws2s(CMfcStrFile::CString2string(strFile));
cv::Mat mSrcBGR = cv::imread(sFilePath.c_str());
if (!mSrcBGR.data)
return;
//是否需要显示或者保存中间结果
int nWinWidth = mSrcBGR.cols;
int nWinHeight = mSrcBGR.rows;
//屏幕边缘保留像素
int nReserved = 40;
int cx = GetSystemMetrics(SM_CXFULLSCREEN) - nReserved;
int cy = GetSystemMetrics(SM_CYFULLSCREEN) - nReserved;
if (nWinWidth > cx || nWinHeight > cy)
{
double dProp = MIN((double)cx / nWinWidth, (double)cy /nWinHeight);
nWinWidth = int(dProp * nWinWidth + 0.5);
nWinHeight = int(dProp * nWinHeight + 0.5);
cv::resize(mSrcBGR, mSrcBGR, cv::Size(nWinWidth, nWinHeight));
}
double dCannyThresh1 = GetDlgItemInt(IDC_EDIT_CANNYTHRESH1);
double dCannyThresh2 = GetDlgItemInt(IDC_EDIT_CANNYTHRESH2);
int nGapTime = GetDlgItemInt(IDC_EDIT_GAPTIME);
if (nGapTime < 0)
{
nGapTime = 500;
SetDlgItemText(IDC_EDIT_GAPTIME, _T("500"));
}
//灰度化,滤波,Canny边缘检测
Mat imageGray;
cvtColor(mSrcBGR, imageGray, CV_BGR2GRAY);//灰度转换
GaussianBlur(imageGray, imageGray, Size(5, 5), 2); //高斯滤波
if (dCannyThresh1 < 0 || dCannyThresh2 < 0)
{
AutoCanny(imageGray, imageGray, dCannyThresh1, dCannyThresh2);
}
else
{
if (dCannyThresh1 < 1.0)
{
dCannyThresh1 = 100.0;
SetDlgItemText(IDC_EDIT_CANNYTHRESH1, _T("100"));
}
if (dCannyThresh2 < 1.0)
{
dCannyThresh2 = 200.0;
SetDlgItemText(IDC_EDIT_CANNYTHRESH2, _T("200"));
}
cv::Canny(imageGray, imageGray, dCannyThresh1, dCannyThresh2);
}
//查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(imageGray, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
cv::Mat mCanvas = mSrcBGR.clone();
for (int i = 0; i < contours.size(); ++i)
{
cv::drawContours(mCanvas, contours, i, cv::Scalar(0, 255, 0));
}
if (hierarchy.size() == 0)
{
//可能检测不到轮廓
return;
}
Mat marks(mSrcBGR.size(), CV_32S); //Opencv分水岭第二个矩阵参数
marks = Scalar::all(0);
int index = 0;
int compCount = 0;
for (; index >= 0; index = hierarchy[index][0], compCount++)
{
//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点
drawContours(marks, contours, index, Scalar::all(compCount + 1), 1, 8, hierarchy);
}
//末尾的图像需要保持5秒
compCount += 5 * 1000 / nGapTime;
int nSeconds = compCount * nGapTime /1000 ;
int nMinutes = nSeconds / 60;
nSeconds = nSeconds % 60;
CString strTipPre;
strTipPre.Format(_T("预计持续时间 %d分%d秒!是否继续?"), nMinutes, nSeconds);
if (MessageBox(strTipPre, _T("提示"), MB_YESNO) == IDNO)
{
ppi->End();
return;
}
watershed(mSrcBGR, marks);
//显示每一个区域
cv::Mat mMask, mCurArea(mSrcBGR.size(), CV_8UC3, cv::Scalar(255.0, 255.0, 255.0));
cv::imshow("mCurArea", mCurArea);
cv::moveWindow("mCurArea", cx / 2 - nWinWidth / 2, cy / 2 - nWinHeight / 2);
double r = 1000.0 / nGapTime;
//获得帧的宽高
Size S(mSrcBGR.cols, mSrcBGR.rows);
std::string strSavePath = CStdStr::ws2s(CStdStr::ReplaceSuffix(sFile, _T(".avi")));
VideoWriter writer(strSavePath.c_str(), -1, r, S);
cv::Mat frame, output;
for (int i = 0; i < compCount; i++)
{
cv::inRange(marks, i + 1, i + 1, mMask);
//膨胀避免缝隙
int an = 1;
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(an * 2 + 1, an * 2 + 1), cv::Point(an, an));
cv::dilate(mMask, mMask, element);
mSrcBGR.copyTo(mCurArea, mMask);
cv::imshow("mCurArea", mCurArea);
writer << mCurArea;
cv::waitKey(10);
}
writer.release();
/*********************************这里增加主程序 结束***************************************/
//结束耗时
int nMin = 0, nSecond = 0, nMilliSecond = 0;
et.End(nMin, nSecond, nMilliSecond);
//结束日志
CLOG::Out("end task!");
CLOG::Out(_T("This task costs %d min %d second %d millisecond!"), nMin, nSecond, nMilliSecond);
CLOG::End();
//结束进度显示
ppi->End();
FlashWindow(TRUE);
WriteIniFile(GetIniPath(), m_cfg);
CString strTips;
strTips.Format(_T("本次耗时 %d分%d秒%d毫秒!"), nMin, nSecond, nMilliSecond);
AfxMessageBox(strTips);
CDialogEx::OnOK();
}
_tstring CDllTestorDlg::GetIniPath(const TCHAR* szFileExt /*= _T(".ini")*/)
{
TCHAR chpath[MAX_PATH];
GetModuleFileName(NULL, chpath, sizeof(chpath));
_tstring strModulePath = CMfcStrFile::CString2string(chpath);
_tstring strIniPath = CStdStr::ReplaceSuffix(strModulePath, szFileExt);
return strIniPath;
这样就可以对于任意图片实现该效果了,有点类似于看一幅画完成的过程,当然了,仅供娱乐。
更多的交流,欢迎留言。