我正在尝试在windows 8应用程序中直接将图像处理效果应用于相机订阅。
我尝试了一种方法,使用画布和重绘图像后,应用效果从网络摄像头直接获得。但是这种方法对于基本效果很好,但是对于像边缘检测这样的效果,它在使用canvas方法时会产生很大的延迟和闪烁。
另一种方法是创建MFT(MediaFoundation Tochange),但是它可以在C中实现,而我对此一无所知。
有谁能告诉我,我怎样才能在windows 8 metro风格的应用程序中实现直接在网络摄像头流上应用效果的目的,要么改进画布方法,这样像边缘检测这样的大型效果就不会有任何问题,要么我怎样才能在c中应用mft,因为我曾经使用过c语言或其他方法?

最佳答案

上周我在这个领域玩了不少,甚至考虑写一篇关于它的博文。我想这个答案也可以。
你可以去MFT方式,这需要在C++中完成,但是你需要写的东西在C和C++之间不会有太大的不同。唯一需要注意的是,我认为mft在yuv颜色空间中工作,所以典型的卷积过滤器/效果可能会有点不同,或者需要转换为rgb。如果您决定在C应用程序端执行该路由,那么您只需要调用mediacapture.addeffectasync()。好吧,你需要编辑你的package.appxmanifest等,但我们先来做第一件事。
如果你看看Media capture using webcam sample-它已经满足了你的需要。它将灰度效果应用到您的相机馈送。它包括一个C++ MFT项目,它应用在C版本中可用的应用程序中。我不得不将效果应用于mediaellent,这可能不是您所需要的,但也很简单-调用mediaellent.addvideoeffect(),您的视频文件播放现在应用灰度效果。要能够使用mft,只需添加对grayscaletransform项目的引用并将以下行添加到appxmanifest:

<Extensions>
  <Extension Category="windows.activatableClass.inProcessServer">
    <InProcessServer>
      <Path>GrayscaleTransform.dll</Path>
      <ActivatableClass ActivatableClassId="GrayscaleTransform.GrayscaleEffect" ThreadingModel="both" />
    </InProcessServer>
  </Extension>
</Extensions>

MFT代码的工作原理:
以下几行创建像素颜色转换矩阵
float scale = (float)MFGetAttributeDouble(m_pAttributes, MFT_GRAYSCALE_SATURATION, 0.0f);
float angle = (float)MFGetAttributeDouble(m_pAttributes, MFT_GRAYSCALE_CHROMA_ROTATION, 0.0f);
m_transform = D2D1::Matrix3x2F::Scale(scale, scale) * D2D1::Matrix3x2F::Rotation(angle);

根据视频源的像素格式-选择不同的转换方法来扫描像素。查找以下行:
m_pTransformFn = TransformImage_YUY2;
m_pTransformFn = TransformImage_UYVY;
m_pTransformFn = TransformImage_NV12;

对于我的示例m4v文件-该格式被检测为nv12,因此它调用transformimage_nv12。
对于指定范围内(m_rcdest)或整个屏幕内(如果未指定范围)的像素,transformimage方法调用transformchroma(mat,&u,&v)。
对于其他像素-将复制原始帧中的值。
transformchroma使用m_变换变换像素。如果您想改变效果-您可以简单地改变m_变换矩阵,或者如果您需要像在边缘检测过滤器中那样访问相邻像素-修改transformimage_u方法来处理这些像素。
这是一种方法。我认为这是相当CPU密集的,所以我个人更喜欢为这样的操作编写一个像素着色器。但是,如何将像素着色器应用于视频流?好吧,我还没有完全到那里,但我相信你可以transfer video frames到一个directx表面相当容易,并调用一个像素着色他们以后。到目前为止-我已经能够转移视频帧,我希望下周应用阴影。我可以写一篇关于它的博文。我把这个类放在一个模板上,然后将它转换成一个库,然后将它与一个C/C/XAML应用程序一起使用,将它与我使用的C语言项目中创建的子类相关联,以显示视频。我不得不在meplayer类中做一些更改。首先-我必须将其移动到一个公共命名空间,使其可用于其他程序集。然后,我不得不将它创建的swapchain修改为可用于swapchainbackgroundpanel的格式:
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
        swapChainDesc.Width = m_rcTarget.right;
        swapChainDesc.Height = m_rcTarget.bottom;
        // Most common swapchain format is DXGI_FORMAT_R8G8B8A8-UNORM
        swapChainDesc.Format = m_d3dFormat;
        swapChainDesc.Stereo = false;

        // Don't use Multi-sampling
        swapChainDesc.SampleDesc.Count = 1;
        swapChainDesc.SampleDesc.Quality = 0;

        //swapChainDesc.BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // Allow it to be used as a render target.
        // Use more than 1 buffer to enable Flip effect.
        //swapChainDesc.BufferCount = 4;
        swapChainDesc.BufferCount = 2;
        //swapChainDesc.Scaling = DXGI_SCALING_NONE;
        swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
        swapChainDesc.Flags = 0;

最后-不是调用createswapchainforcewindow-我调用createswapchainforcomposition并将swapchain与我的swapchainbackgroundpanel关联:
        // Create the swap chain and then associate it with the SwapChainBackgroundPanel.
        DX::ThrowIfFailed(
            spDXGIFactory.Get()->CreateSwapChainForComposition(
                spDevice.Get(),
                &swapChainDesc,
                nullptr,                                // allow on all displays
                &m_spDX11SwapChain)
            );

        ComPtr<ISwapChainBackgroundPanelNative> dxRootPanelAsSwapChainBackgroundPanel;

        // Set the swap chain on the SwapChainBackgroundPanel.
        reinterpret_cast<IUnknown*>(m_swapChainPanel)->QueryInterface(
            IID_PPV_ARGS(&dxRootPanelAsSwapChainBackgroundPanel)
            );

        DX::ThrowIfFailed(
            dxRootPanelAsSwapChainBackgroundPanel->SetSwapChain(m_spDX11SwapChain.Get())
            );

*编辑跟随
又忘了一件事。如果你的目标是保持在纯c-如果你知道如何将帧捕获到一个可写的libitmap(可能是通过使用memorystream调用Media engine native C++ playback sample,然后在流中调用writeablebitmap.MediaCapture.CapturePhotoToStreamAsync()),你可以使用SetSource()来处理你的图像。这可能不是最好的性能,但如果您的分辨率不是太高或您的帧速率要求不是很高-这可能只是足够的。codeplex上的项目还没有正式支持winrt,但是我有一个可以运行的版本,您可以尝试WriteableBitmapEx

08-25 13:11
查看更多