Motion.cpp
上传用户:panpan8800
上传日期:2013-06-29
资源大小:274k
文件大小:11k
源码类别:

图形图像处理

开发平台:

Visual C++

  1. #include "GlobalApi.h"
  2. #include "stdafx.h"
  3. #include "cdib.h"
  4. #include "math.h"
  5. /*************************************************************************
  6.  *
  7.  * 函数名称:
  8.  *   GetFileName()
  9.  *
  10.  * 输入参数:
  11.  *   CString strFilePathName - 图象的文件名 
  12.  *   int nCurFrameNum - 当前帧的图象文件名
  13.  *
  14.  * 返回值:
  15.  *   CString - 返回给定帧数的图象文件名
  16.  *
  17.  * 说明:
  18.  *   该函数根据指定文件路径名和当前图象序列的帧数获取图象文件名
  19.  *   该函数中需要注意的是,只能读取0-999帧图象,图象为bmp格式,且按照
  20.  *   帧数数字进行存储,例如第一帧图象文件名为×××001.bmp,第33帧图象
  21.  *   的文件名为×××033.bmp。如果不是bmp文件,则返回"NULL"。
  22.  *
  23.  *************************************************************************
  24.  */
  25. CString GetFileName(CString strFilePathName, int nCurFrameNum)
  26. {
  27. //文件的路径名
  28. CString strTempFileName;
  29. int nNumPos=strFilePathName.Find(".");
  30. if(nNumPos==-1){
  31. AfxMessageBox("Please choose a bmp file");
  32. return "NULL";
  33. }
  34. //表示去掉了扩展名和数字标号的路径名,在这里,限定帧数为0~999,所以采用三位来表示
  35. CString strFileNameNoExtNoNum=strFilePathName.Left(nNumPos-3);
  36. //表示标号的字符串
  37. CString strTempNum;
  38. if(nCurFrameNum<10){
  39. strTempNum.Format("00%d",nCurFrameNum);
  40. }
  41. else {
  42. if(nCurFrameNum<100 &&nCurFrameNum>=10){
  43. strTempNum.Format("0%d",nCurFrameNum);
  44. }
  45. else{
  46. strTempNum.Format("%d",nCurFrameNum);
  47. }
  48. }
  49. // 得到图象文件名
  50. strTempFileName=strFileNameNoExtNoNum+strTempNum+".bmp";
  51. // 返回
  52. return strTempFileName;
  53. }
  54. /*************************************************************************
  55.  *
  56.  * 函数名称:
  57.  *   LoadDibSeq()
  58.  *
  59.  * 输入参数:
  60.  *   CString strFilePath - 第一帧图象的文件名 
  61.  *   int nCurFrameNum - 当前帧的图象文件名
  62.  *   int nTotalFrameNum - 进行检测的图象帧数
  63.  *   CDib* pDib - 指向返回CDib类的指针
  64.  *
  65.  * 返回值:
  66.  *   BOOL - 成功则返回TRUE,否则返回FALSE
  67.  *
  68.  * 说明:
  69.  *   该函数根据指定文件路径名和当前图象序列的帧数读取图象数据道pDib中
  70.  *   该函数中需要注意的是,只能读取0-999帧图象,图象为bmp格式,且按照
  71.  *   帧数数字进行存储,例如第一帧图象文件名为×××001.bmp,第33帧图象
  72.  *   的文件名为×××033.bmp。
  73.  *
  74.  *************************************************************************
  75.  */
  76. BOOL LoadDibSeq(CString strFilePath, int nCurFrameNum, int nTotalFrameNum, CDib* pDib)
  77. {
  78. //一般来讲,程序在处理的过程中需要装载的帧号应该是由外界指定的
  79. //当指定的帧号不合法时,就装载当前帧作为默认值
  80. if (nCurFrameNum<1 || nCurFrameNum>nTotalFrameNum)
  81. {
  82. AfxMessageBox("Invalidate file frame number");
  83. return FALSE;
  84. }
  85. // 获得当前帧的图象文件名
  86. CString strTempFileName;
  87. strTempFileName=GetFileName(strFilePath,nCurFrameNum);
  88. CFile fileOpen=NULL;
  89. // 打开文件并读取
  90. fileOpen.Open(strTempFileName,CFile::modeRead);
  91. if(pDib->Read(&fileOpen)==FALSE){
  92. AfxMessageBox("can not open the file "+strTempFileName);
  93. return FALSE;
  94. }
  95. return TRUE;
  96. }
  97. /*************************************************************************
  98.  *
  99.  * 函数名称:
  100.  *   BinaFrameDiff()
  101.  *
  102.  * 输入参数:
  103.  *   unsigned char* pUnchImg1 - 图象的文件名 
  104.  *   unsigned char* pUnchImg2 - 当前帧的图象文件名
  105.  *   int nWidth
  106.  *   int nHeight
  107.  *   unsigned char* pUnchResult
  108.  *   int nThreshold
  109.  *
  110.  * 返回值:
  111.  *   CString - 返回给定帧数的图象文件名
  112.  *
  113.  * 说明:
  114.  *   该函数比较pUnchImg1和pUnchImg2两个区域中的内容,如果两个区域内     
  115.  *容的差值的绝对值比Threshold大,则将pUnchResult相应的元素设置为逻辑值1,
  116.  *用灰度255表示,否则为0,并用灰度0表示
  117.  *
  118.  *************************************************************************
  119.  */
  120. void BinaFrameDiff(unsigned char *pUnchImg1, unsigned char *pUnchImg2, 
  121.   int nWidth, int nHeight, unsigned char * pUnchResult,
  122.   int nThreshold)
  123. {
  124. int nTemp=0;
  125. for (int i=0;i<nHeight*nWidth;i++) {
  126. nTemp = abs(pUnchImg1[i] - pUnchImg2[i]);
  127. pUnchResult[i] = nTemp > nThreshold ? 255:0;
  128. return ;
  129. }
  130. /*************************************************************************
  131.  *
  132.  * 函数名称:
  133.  *   ErodeFrameDiff()
  134.  *
  135.  * 输入参数:
  136.  *   unsigned char* pUnchImg1 - 图象数据指针
  137.  *   int nWidth - 图象宽度
  138.  *   int nHeight - 图象高度
  139.  *   int nErodeHalfWin - 腐蚀窗口大小的一半
  140.  *   unsigned char* pUnchResult - 结果数据制止
  141.  *   int nThreshold - 阈值
  142.  *
  143.  * 返回值:
  144.  *   无
  145.  *
  146.  * 说明:
  147.  *   该函数进行腐蚀操作,形态学操作对pUnchImg中的每一点,计算这一点对应的
  148.  *窗口内的一些参数,然后根据参数结果给这个点设置相应的值. 功能上相当于广义滤波
  149.  *
  150.  *************************************************************************
  151.  */
  152. void ErodeFrameDiff(unsigned char *pUnchImg, int nWidth, int nHeight, int nErodeHalfWin, 
  153.   int nErodeThreshold, unsigned char *pUnchResult)
  154. {
  155. // 搜索整个图象,对图象进行腐蚀处理
  156. for (int i=nErodeHalfWin;i<nHeight-nErodeHalfWin;i++) {
  157. for (int j=nErodeHalfWin;j<nWidth-nErodeHalfWin;j++) {
  158. // 如果帧间的差不为0才进行处理
  159. if (pUnchImg[i*nWidth+j] != 0) 
  160. {
  161. int iPointCount = 0;
  162. // 根据此点的邻域判断此点是否需要删除
  163. for (int r=-nErodeHalfWin;r<=nErodeHalfWin;r++) {
  164. for (int c=-nErodeHalfWin;c<=nErodeHalfWin;c++) {
  165. if (pUnchImg[(i+r)*nWidth+j+c] != 0) {
  166. iPointCount++;
  167. }
  168. }
  169. }
  170. // 如果邻域中不为0的个数小于设定的阈值,则强行设置为0
  171. if (iPointCount < nErodeThreshold) {
  172. pUnchResult[i*nWidth+j] = 0;
  173. }
  174. else {
  175. pUnchResult[i*nWidth+j] = 255;
  176. }
  177. }
  178. else 
  179. {
  180. pUnchResult[i*nWidth+j] = 0;
  181. }
  182. }
  183. }
  184. return ;
  185. }
  186. /*************************************************************************
  187.  *
  188.  * 函数名称:
  189.  *   GetBackground()
  190.  *
  191.  * 输入参数:
  192.  *   CString strFilePath - 第一帧图象的文件名 
  193.  *   int nTotalFrameNum - 进行检测的图象帧数
  194.  *   int nImageWidth - 图象宽度
  195.  *   int nImageHeight - 图象高度
  196.  *   unsigned char * pUnchBackGround - 指向返回背景数据的指针
  197.  *
  198.  * 返回值:
  199.  *   BOOL - 成功则返回TRUE,否则返回FALSE
  200.  *
  201.  * 说明:
  202.  *   该函数根据指定文件名的图象序列求取静止背景
  203.  *
  204.  *************************************************************************
  205.  */
  206. BOOL GetBackground(CString strFilePath, int nTotalFrameNum, int nImageWidth, 
  207.    int nImageHeight, unsigned char* pUnchBackGround)
  208. {
  209. // 如果此时背景已经生成,函数返回,不需要再一次计算
  210. /*if (pUnchBackGround!=NULL){
  211. return TRUE;
  212. }*/
  213. // pUnchTemp1和pUnchTemp2用来计算相邻两帧之间的帧差
  214. // 每次只要读入一帧即可,即:假设刚刚比较k-1和k帧,那么现在比较k和
  215. // k+1帧,那么k帧是不需要重新读入的
  216. unsigned char* pUnchTemp1;
  217. unsigned char* pUnchTemp2;
  218. pUnchTemp1 = new unsigned char[nImageWidth * nImageHeight * sizeof(unsigned char)];
  219. pUnchTemp2 = new unsigned char[nImageWidth * nImageHeight * sizeof(unsigned char)];
  220. // 临时存放图象数据的CDib指针
  221. CDib* pDibTemp;
  222. pDibTemp = new CDib;
  223. // 读出第一帧数据并放入pDibTemp
  224. pDibTemp->Empty();
  225. if(!LoadDibSeq(strFilePath,1,nTotalFrameNum,pDibTemp)){
  226. return FALSE;
  227. }
  228. // 然后将数据取出,存放在pUnchTemp1中
  229. memcpy(pUnchTemp2,pDibTemp->m_lpImage,nImageWidth*nImageHeight*sizeof(unsigned char));
  230. // pChResultAfterMor 是用来记录帧间变化的内存区域
  231. unsigned char * pUnchTrackBox = new unsigned char[(nTotalFrameNum)*
  232. nImageWidth*nImageHeight*sizeof(unsigned char)];
  233. unsigned int index = 0;
  234. // 帧间差的区域,二进制
  235. unsigned char *pUnchTemp3=new unsigned char[nImageWidth*nImageHeight*sizeof(unsigned char)];
  236. // 腐蚀之后的区域,二进制
  237. unsigned char * pUnchResultAfterMor = new unsigned char[nImageWidth*nImageHeight*sizeof(unsigned char)];
  238. // 对每一帧进行比较
  239. for (int i = 2; i<nTotalFrameNum-1; i++) {
  240. // 打开第i帧图象文件,并将图象存放在CDib对象pDibTemp中
  241. pDibTemp->Empty();
  242. if(!LoadDibSeq(strFilePath , i , nTotalFrameNum , pDibTemp)){
  243. return FALSE;
  244. }
  245. // 然后将数据取出,存放在pUnchTemp2中
  246. memcpy(pUnchTemp2,pDibTemp->m_lpImage,nImageWidth*nImageHeight);
  247. // 对图象帧差进行二值化处理,并将二值化后的图象存放在pUnchTemp3中
  248. BinaFrameDiff(pUnchTemp1,pUnchTemp2 ,nImageWidth,nImageHeight,pUnchTemp3,10);
  249. // 对二值化后的图象进行腐蚀处理,在这里对腐蚀窗口的大小设置为2,阈值为7
  250. ErodeFrameDiff(pUnchTemp3,nImageWidth,nImageHeight,2,7,pUnchResultAfterMor);
  251. // 将此二值化后的程序放入pUnchTrackBox的相应位置
  252. memcpy(pUnchTrackBox+index,pUnchResultAfterMor,sizeof(unsigned char)*nImageWidth*nImageHeight);
  253. // 计算图象数据在pUnchTrackBox中的偏移量
  254. index = index + nImageWidth*nImageHeight*sizeof(unsigned char);
  255. // 每做完两帧之间的比较,就使帧号下移一个,pUnchTemp1中是存k帧内容,pUnchTemp2帧是存k+1
  256. // 帧内容,所以,每次只要把pUnchTemp2中的内容给pTemp1,而pTemp2重新读入既可以了.
  257. unsigned char* pUnchTag = NULL;
  258. pUnchTag   = pUnchTemp1;
  259. pUnchTemp1 = pUnchTemp2;
  260. pUnchTemp2 = pUnchTag;
  261. }
  262. // 释放已分配内存
  263. delete []pUnchTemp1;
  264. pUnchTemp1 = NULL;
  265. delete []pUnchTemp2;
  266. pUnchTemp2 = NULL;
  267. delete []pUnchTemp3;
  268. pUnchTemp3 = NULL;
  269. delete []pUnchResultAfterMor;
  270. pUnchResultAfterMor=NULL;
  271. // 每一帧的大小
  272. int nFrameSize = nImageWidth * nImageHeight * sizeof(unsigned char);
  273. // 记录最大长度
  274. int * pnTrackSegLen = new int [nImageWidth*nImageHeight];
  275. // 记录最大长度中的中间帧标号
  276. int * pnTrackSegFrame = new int [nImageWidth*nImageHeight];
  277. // 对每一个象素点跟踪最大为0的长度,并将最大长度中的中间帧标号记录下来
  278. for (int y = 0; y<nImageHeight; y++) {
  279. for (int x = 0; x<nImageWidth; x++) {
  280. // 此象素在一帧那相对于该帧第一个元素的偏移量
  281. int offset = y * nImageWidth + x;
  282. // 初始化最大长度
  283. int largeLen = 0;
  284. int t = 1;
  285. // 连续为0的段的开始和结束帧标号
  286. int segStart,segEnd;
  287. // 跟踪长度
  288. int segLen;
  289. // 当前帧数
  290. int frameNum;
  291. segLen = 0;
  292. frameNum = 1;
  293. // 遍历整个序列,跟踪并记录此点连续为0的最大长度
  294. while (t < nTotalFrameNum - 1) {
  295. // 如果还没有到达序列结束并且此点的不为0,则继续到下一帧搜索
  296. while ((t < nTotalFrameNum -1) && (pUnchTrackBox[t*nFrameSize+offset] != 0))
  297. t++;
  298. //如果此时的t>= nTotalFrameNum - 1,则说明,已经遍历到最后一帧了
  299. if (t >= nTotalFrameNum - 1)
  300. break;
  301. // 此时应为此长度的开始
  302. segStart = t;
  303. while ((t < nTotalFrameNum - 1) && (pUnchTrackBox[t*nFrameSize+offset] == 0))
  304. t++;
  305. // 此长度的结束帧标号
  306. segEnd = t - 1;
  307. // 获得此连续为0的帧的长度
  308. segLen = segEnd +1 -segStart;
  309. // 判断是否为最大长度,是则进行替换
  310. if (segLen > largeLen) {
  311. largeLen = segLen;
  312. frameNum = (segEnd + segStart)/2;
  313. }
  314. pnTrackSegLen[offset] = largeLen;
  315. pnTrackSegFrame[offset] = frameNum;
  316. }
  317. }
  318. delete []pUnchTrackBox; pUnchTrackBox=NULL;
  319. // 因为对每个象素而言,背景可能出现在不同帧里,此时需要把所有帧调入内存
  320. unsigned char* pBuffer = new unsigned char[nTotalFrameNum*(nImageWidth*nImageHeight)];
  321. for (int k=1; k<nTotalFrameNum; k++) {
  322. pDibTemp->Empty();
  323. LoadDibSeq(strFilePath , k , nTotalFrameNum , pDibTemp);
  324. // 然后将数据取出,存放在pBuffer的相应位置中
  325. memcpy(pBuffer+k*nFrameSize,pDibTemp->m_lpImage,nImageWidth*nImageHeight);
  326. }
  327. // 遍历整个图象,设置背景数据
  328. for (y=0; y<nImageHeight; y++) {
  329. for (int x=0; x<nImageWidth; x++) {
  330. // 获得此象素点在一帧数据中的偏移量
  331. int k = y * nImageWidth + x;
  332. // 获得此象素点连续为0的最大长度的中间帧标号
  333. int nFrameMax = pnTrackSegFrame [k];
  334. // 设置具有最大长度的中间帧的数据为背景数据
  335. pUnchBackGround[k] = pBuffer[(nFrameMax*nFrameSize)+k];
  336. }
  337. }
  338. delete []pnTrackSegLen; 
  339. pnTrackSegLen=NULL;
  340. delete []pnTrackSegFrame;
  341. pnTrackSegFrame=NULL;
  342. delete []pBuffer; 
  343. pBuffer=NULL;
  344. ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
  345. return TRUE;
  346. }