基于灰度颜色个数的视频截图选取(1)

发表于:2007-06-11来源:作者:点击数: 标签:
前几天在帮师兄做一个视频截图的模块,采用了DirectShow的接口来访问视频文件。 开发 工具使用的是Visual C++ 2005 Express和Visual C# 2005 Express,VC++写的一个封装了对DirectShow的接口访问的DLL,然后在C#做的界面程序里面调用。 1. 关于DirectShow的

前几天在帮师兄做一个视频截图的模块,采用了DirectShow的接口来访问视频文件。开发工具使用的是Visual C++ 2005 Express和Visual C# 2005 Express,VC++写的一个封装了对DirectShow的接口访问的DLL,然后在C#做的界面程序里面调用。

1. 关于DirectShow的视频截图方法

DirectShow以前是属于DirectX内的一个部分,后来Microsoft把DirectShow归入了Platform SDK内了。关于DirectShow如何来截取视频文件内部的图片picture,在网上可以搜索到很多。在MSDN关于DirectShow SDK的教程里面,也有专门举例如何使用DirectShow的IMediaDet接口来截视频流内的截图的:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcedshow/html/_dxce_dshow_directshow.asp

使用DirectShow来访问视频文件就可以避免去了解各种视频压缩文件格式,编码格式等等很繁琐甚至是困难的问题了。

2. 选择哪个时间点来截视频图片呢?

但是,选取哪个时间点的视频图片呢?我们在看Windows浏览器里面视频文件的微缩图都是视频文件的第一帧,但是如果第一帧是全黑或者全白呢?那么我们看到的这个截下来的视频图片并没有任何意义。甚至比如电影开头的演员字幕等帧,对于观众来说都没有多大的意义。一部电影的截图选择,如果按照精彩镜头来分,那么需要计算机去理解该电影的内容,这个工作在现阶段来说,涉及到计算机视觉,数字图像,人工智能等前沿技术,不大可能做得出来。

考虑到全黑,全白,以及片头字幕等没有意义的帧图片的特点,就是颜色个数相对较少,相对单调。于是,可以通过一个颜色个数的阈值,来对所有帧图片进行筛选。将颜色个数小于阈值的剔除。一般使用颜色丰富的图片,肯定帧图片更加丰富。

但是,在24位真彩色中,R,G,B都是0-255,任何一个分量相差了一点点,视觉上来说,差异并不大,但是对于计算机来说,就完全是两个颜色了,这种过于精确的颜色统计,对于人来说并不见得好。于是,我选择使用颜色的灰度值来代替真彩色RGB的统计。关于RGB到灰度值的公式,选择的是最简单的:

GRAY(灰度) = (R+G+B)/3

3. 实现一个测试算法的Demo

好了,大体的截图选取算法思想就是这样了。下面我就一步一步来把这个算法实现的Demo,通过Visual C++ 2005 Express和Visual C# 2005 Express开发工具做出来。

首先是做封装DirectShow的Win32 DLL。

Microsoft那里下载的Visual C++ 2005 Express并没有附带Platform SDK,Windows的最新Platform SDK可以直接从Microsoft的MSDN那里下载到(我选择的是Windows 2003 Server RC2)。按照MSDN上所述的,搭建起Visual C++ 2005 Express内的Platform SDK设置后就可以开发Win32的程序了。

下面是封装的DLL的程序代码:

// MovieGrabberDLL.cpp : 定义 DLL 应用程序的入口点。

//



#include "stdafx.h"

#include "MovieGrabberDLL.h"

BOOL APIENTRY DllMain( HANDLE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved

)

...{

switch (ul_reason_for_call)

...{

case DLL_PROCESS_ATTACH:

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}



/**//**

* 抓取视频的截图

* @param aPath 视频文件的位置

* @return

*/

MOVIEGRABBERDLL_API HANDLE GrabMovieFrame(LPCTSTR aPath,int grayColorCountThreshold)

...{

HRESULT hr;

// 定义IMediaDet接口实例

CComPtr< IMediaDet > pDet;

hr = pDet.CoCreateInstance(__uuidof(MediaDet));

if (FAILED(hr))

return NULL;



// 将影片文件名转换成BSTR类型

CComBSTR openBSTR(aPath);

// 设置IMediaDet接口的文件关联

hr = pDet->put_Filename(openBSTR);

if (FAILED(hr))

return NULL;



// 从影片中检索视频流和音频流

long lStreams;

hr = pDet->get_OutputStreams(&lStreams);

if (FAILED(hr))

return NULL;



// 取出影片的视频流,因为帧的信息是保存在视频流中的

bool bFound = false;

for (int i=0; i
...{

GUID major_type;

hr = pDet->put_CurrentStream(i);

if (SUCCEEDED(hr))

hr = pDet->get_StreamType(&major_type);

if (FAILED(hr))

break;

if (major_type == MEDIATYPE_Video)

...{

bFound = true;

break;

}

}

if (!bFound)

return NULL;



long width = 0, height = 0; // 存储位图的宽和高(单位:象素)

AM_MEDIA_TYPE mt;

hr = pDet->get_StreamMediaType(&mt);

if (SUCCEEDED(hr))

...{

if ((mt.formattype == FORMAT_VideoInfo) &&

(mt.cbFormat >= sizeof(VIDEOINFOHEADER)))

...{

// 得到VIDEOINFOHEADER结构指针,VIDEOINFOHEADER结构包含一些与视频

// 有关的信息,其中含有BITMAPINFORHEADER结构

VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);

width = pVih->bmiHeader.biWidth;

height = pVih->bmiHeader.biHeight;

if(height < 0 ) height *= -1;

}

else

hr = VFW_E_INVALIDMEDIATYPE;

MyFreeMediaType(mt); // 释放AM_MEDIA_TYPE结构

}

if (FAILED(hr))

return NULL;

return (HANDLE)LookforSuitableMovieFrame(pDet,width,height,grayColorCountThreshold);

}



/**//**

* 写入合适视频帧截图到磁盘

* @param pDet DirectShow的IMediaDet接口

* @param width 截图的长

* @param height 截图的宽

* @param grayColorCountThreshold 灰度颜色个数阈值

*/

HBITMAP LookforSuitableMovieFrame(IMediaDet* pDet,int width,int height,int grayColorCountThreshold)

...{

long size;

double time = 0.0;

double totaltime;



// 获取整个视频的时间长度

pDet->get_StreamLength(&totaltime);

// 每1秒,截取视频截图

for(time=0.0; time      ...{

// 获取bitmap的buffer大小

HRESULT hr = pDet->GetBitmapBits(time, &size, 0, width, height);

if (SUCCEEDED(hr))

...{

char *pBuffer = new char[size];

if (!pBuffer)

return NULL;

hr = pDet->GetBitmapBits(time, 0, pBuffer, width, height);

if (SUCCEEDED(hr))

...{

// Find the address of the start of the image data.

void *pData = pBuffer + sizeof(BITMAPINFOHEADER);

if(IsSuitableMovieFrame(pData,width,height,grayColorCountThreshold))

...{

BITMAPINFOHEADER *bmih = (BITMAPINFOHEADER*)pBuffer;

HDC hdcDest = GetDC(0);



BITMAPINFO bmi;

ZeroMemory(&bmi, sizeof(BITMAPINFO));

CopyMemory(&(bmi.bmiHeader), bmih, sizeof(BITMAPINFOHEADER));

HBITMAP hBitmap = CreateDIBitmap(hdcDest, bmih, CBM_INIT,

pData, &bmi, DIB_RGB_COLORS);



delete[] pBuffer;

return hBitmap;

}

}

delete[] pBuffer;

}

}

return NULL;

}



/**//**

* 检测一个位图是否是合适的视频截图

* @param pData 位图的点色数组

* @param width 位图的长

* @param height 位图的宽

* @param grayColorCountThreshold 灰度颜色个数阈值

*/

bool IsSuitableMovieFrame(void* pData,int width,int height,int grayColorCountThreshold)

...{

BYTE* pixels = (BYTE*)pData;

int numGrayColor = 0;

int size = width*height;

int graycolor;

int i,j;

int* appearedcolors = new int[grayColorCountThreshold];

int numappearedcolors = 0;

for(i=0;i     ...{

// 计算当前点的灰度值,采用的RGB转换灰度的公式是GRAY = (R+G+B)/3

graycolor = (pixels[i*3] +pixels[i*3+1]+pixels[i*3+2])/3;

// 检测该灰度色是否之前出现过

for(j=0;j         ...{

if(graycolor == appearedcolors[j])

break;

}

if(j == numappearedcolors) // 如果是新的灰度颜色值

...{

numappearedcolors++;

if(numappearedcolors == grayColorCountThreshold) // 如果灰度颜色个数满足阈值

...{

delete[] appearedcolors;

return true; // 返回信息,合适

}

else

...{

appearedcolors[j] = graycolor; // 记录下该灰度颜色值

}

}

}

delete[] appearedcolors;

return false; // 返回信息,不合适

}



void MyFreeMediaType(AM_MEDIA_TYPE& mt)

...{

if(mt.cbFormat != 0)

...{

CoTaskMemFree((PVOID)mt.pbFormat);

mt.cbFormat = 0;

}



if (mt.pUnk != NULL)

...{

mt.pUnk->Release();

mt.pUnk = NULL;

}

}


共2页: 1 [2] 下一页

原文转自:http://www.ltesting.net

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
...