电信主站 网通分站
购买流程 付款方式 常见问题 在线提问 续租服务 购物车
用户名: 密 码: 忘记密码?
首 页
域名注册
虚拟主机
双线主机
服务器租用
VPS主机
企业邮局
代理专区
客服中心
虚拟主机行业资讯 虚拟主机评测对比 互联网最新动态 技术学院 站长资讯 在线教程 网站运营
搜索优化 服务器 网络编程 图形图象 站长之家 网页制作 操作系统
冲浪宝典 软件教学 视频通信 办公软件 邮件系统 网络安全 认证考试
您当前位置:西部数码->资讯中心-> 在线教程-> .NET
基于灰度颜色个数的视频截图选取-.NET教程,C#语言
作者:网友供稿 点击:4
  西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!虚拟主机可在线rar解压,自动数据恢复设置虚拟目录等.虚拟主机免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金!
文章页数:[1] 
前几天在帮师兄做一个视频截图的模块,采用了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<lstreams; 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 <totaltime; time+= 1.0
     
{
         
// 获取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<size; i++
     
{
         
// 计算当前点的灰度值,采用的rgb转换灰度的公式是gray = (r+g+b)/3
         graycolor = (pixels[i*3+pixels[i*3+1]+pixels[i*3+2])/3;
         
// 检测该灰度色是否之前出现过
         for(j=0;j<numappearedcolors; 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;
     }

}
 
其中,为了使用directshow,我们除了需要windows.h外,还需要dshow.h,qedit.h和atlbase.h三个头文件,最后再加上一个strmiids.lib库文件。
 
接下来就开启visual c# 2005 express来做一个简单的界面程序。为什么选择c# 来开发界面程序呢?原因很简单,因为c#很简单,同时visual c# 2005 express这样免费又功能强大的工具可以使用。
界面程序很简单,就下面这个样子:
 
       c# 部分调用前面写好的dll函数,实现ddshow的抓图。 moviegrabberdll.cs源代码如下:
using system;
using system.collections.generic;
using system.text;
using system.runtime.interopservices;
using system.drawing;
 
namespace moviegrabbercsharp
{
    
class moviegrabberdll
    
{
        [dllimport(
"moviegrabberdll.dll")]
        
public static extern int fnmoviegrabberdll();
 
        [dllimport(
"moviegrabberdll.dll")]
        
public static extern intptr grabmovieframe(string apath, int graycolorcountthreshold);
 
        
public static bitmap grabmovieframebitmap(string apath,int graycolorcountthreshold)
        
{
            intptr hbitmap 
= grabmovieframe(apath, graycolorcountthreshold);
            
if(hbitmap == intptr.zero)
                
return null;
            
return bitmap.fromhbitmap(hbitmap);
        }

 
        
public static bitmap grabmovieframebitmap(string apath)
        
{
            
return grabmovieframebitmap(apath, 8);
        }

    }

}

 
窗口类mainform.cs的源代码如下:
using system;
using system.collections.generic;
using system.componentmodel;
using system.data;
using system.drawing;
using system.text;
using system.windows.forms;
 
namespace moviegrabbercsharp
{
    
public partial class mainform : form
    
{
        
public mainform()
        
{
            initializecomponent();
        }

 
        
private void openmoviefilepathbutton_click(object sender, eventargs e)
        
{
            openfiledialog dlg 
= new openfiledialog();
            
if (dlg.showdialog() == dialogresult.ok)
            
{
                moviefilepathtextbox.text 
= dlg.filename;
            }

 
        }

 
        
private void grabberbutton_click(object sender, eventargs e)
        
{
            bitmap bitmap 
= moviegrabberdll.grabmovieframebitmap(moviefilepathtextbox.text);
            
if (bitmap != null)
            
{
                messagebox.show(
"抓图成功!");
                grabberpicturebox.sizemode 
= pictureboxsizemode.stretchimage;
                grabberpicturebox.image 
= bitmap;
                grabberpicturebox.invalidate();
                grabberpicturebox.refresh();
            }

            
else
            
{
                messagebox.show(
"失败!");
            }

        }

 
        
private void exitbutton_click(object sender, eventargs e)
        
{
            
this.close();
        }

    }

}

 
编译完成后,我们使用windows里面的一个intro.wmv视频文件来做测试,具体路径是:c:\windows\system32\oobe\imagee\intro.wmv。之所以选择这个文件作为视频测试文件,因为这个视频是大家安装完成后winxp后都会自动启动的windows xp的介绍视频,而且这个视频的开始部分是全黑,然后渐渐变亮,再到windows xp的动画部分。如果用windows自带的浏览器看微缩图显示,就是下面这个结果:
       可以看到,这个intro.wmv的微缩图是完全的一张黑色图片,我们并不能看到任何关于视频文件有意义的内容。
       下面启动我们刚才编写的demo视频截图工具来截一下图片,同样这个视频文件,可以看到这个的结果。
      
 
       其中,程序里面默认给出的灰度颜色个数阈值是8,那么就是说,至少图片要有8个不同的颜色灰度值才会截取,而之前的全黑,全白就自然滤过了。
 
 
 
 

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!
相关主题
文章页数:[1]