65.9K
CodeProject 正在变化。 阅读更多。
Home

原型化 DirectShow 过滤器 C#

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.42/5 (6投票s)

2005年12月14日

4分钟阅读

viewsIcon

296393

downloadIcon

3713

本文展示了如何在 C# 中实现 DirectShow 过滤器。

Sample Image

引言

DirectShow 过滤器是 Windows 平台上多媒体应用程序的基本构建块。通常,它们是用 C++ 编写的。在本文中,我将展示如何在 C# 中实现几个 DirectShow 转换过滤器。由于 Microsoft 不建议使用托管代码来实现过滤器,我将称它们为原型,但它们在大多数情况下表现良好。我们将开发 Sobel 变换作为 DirectShow 过滤器。Sobel 变换通常用于视频分割,视频分割是在场景中识别对象的过程。

背景

我们在 C# 中实现 DirectShow 过滤器的策略依赖于拥有一个 C++ 类,该类包含指向实现我们过滤器的托管对象的指针。C++ 类只是将所有处理转发到托管对象。C++ 类是使用 Cutler 的 DirectShow 过滤器向导生成的。我们只需清理向导生成的代码,以包含指向我们的 C# 对象的指针,并将所有过滤器处理实现为托管对象的方法。

我编写了这个 Sobel 过滤器来了解我们使用 C# 可以获得的性能类型。如果你想“让你的 CPU 转一转”,这是一个很好的测试。正如编写的那样(即,没有进行优化),每个像素大约有 20 次整数乘法。通常,您会在将 Sobel 变换应用于图像之前将图像转换为黑白。所以我还编写了一个转换过滤器,将 RGB24 位图转换为黑白。

使用代码

如果你只想拥有一个实现 Sobel 变换的过滤器,你可以下载并安装托管的 DLL 和 Ax 文件(从下载链接)。然后在注册 Ax 文件并将托管 DLL 安装到全局程序集缓存 (GAC) 后,你可以启动 GraphEdit 实用程序并添加“CsSobelV2”过滤器(在 GraphEdit 的 DirectShow 过滤器类别中找到)。然后渲染媒体文件以查看视频中帧的黑白“草图”。 (请注意,开源 DirectShowLib 也必须位于 GAC 中。)更多信息可以在下载中包含的“readme.txt”文件中找到。

如果你想使用这些过滤器作为起点来实现你自己的过滤器,你可以修改 MySobelV2 类的 TransformProcessFrame 方法。 如果您对视频帧的单一媒体类型(此处为 Rgb24)感到满意,并且您可以接受 C++ DirectShow 基类的默认内存分配器(这涵盖了很多情况),那么这已经足够了。

Sobel 变换

Sobel 变换使用以下两个矩阵

  // define the horizontal Sobel mat
  int [,] hx = new  int [,] { {-1, 0, 1}, 
                    {-2, 0, 2}, {-1, 0, 1} };

  // define the vertical Sobel mat
  int[,] hy = new int [,]  { { 1,  2,  1}, 
                  { 0,  0,  0}, {-1, -2, -1} };

并包含以下代码,位于 ProcessFrame 中,该代码对视频的每一帧中的每个像素重复执行

        gradX = 0;
        gradY = 0;
        for (int row = -1; row <= 1; row++)
        {
          for (int col = -1; col <= 1; col++)
          {
            position = GetPosition( x + col, y + row ); 
            intensity = pwSource[position];
            gradX += (intensity * hx[col + 1,row + 1]);
            gradY += (intensity * hy[col + 1,row + 1]); 
          }
        }
         
        // compute the square of the gradient        
        gradMag = gradX * gradX + gradY * gradY;

        // threshold default at 128
        gradMag = (gradMag < threshold*threshold) ? 255 : 0;
               
        // assign the pixel to the destination
        position = GetPosition( x, y );
        pwTarget[position] = pwTarget[position+1] = 
              pwTarget[position+2] = (byte)gradMag;

变换背后的直觉相对简单。由于对象的轮廓由图片中颜色快速变化的区域组成,因此 Sobel 变换会放大这些差异,并将那些变化太小的像素简单地设置为白色(而其他的像素设置为黑色)。水平 Sobel 变换查看左侧和右侧相邻的像素,并将这些值乘以 2 或 -2,它还会查看右上角和左上角,以及右下角和左下角的像素。在对所有这些值求和后,您可以计算欧几里得距离的平方,并使用任意阈值来划分这些值。

关注点

我还包含了用于未压缩 Rgb24 视频帧的黑白转换过滤器的代码。正如所提到的,通常你会先执行此转换,然后再将结果馈送到 Sobel 过滤器。因此,你可以测试具有两个用托管代码编写的过滤器的过滤器图的性能。

过滤器属性页在 GraphEdit 等实用程序中使用时非常方便。但是能够从 C#(轻松地)访问自定义过滤器属性甚至更有用。因此,我修改了 Cutler 的向导生成的 C++ 代码,以包含可以在 C# 中更改的属性。可以进行类似的更改来实现不同的属性。

局限性和已知问题

过滤器接受的媒体类型是未压缩的 Rgb24 视频。由于这是最“自然”的媒体类型之一,因此它不是一个很大的限制,但这些过滤器并非旨在非常灵活。它们只是用于实验的原型。

© . All rights reserved.