如何在Windows8应用程序中在相机流上应用边缘检测等图像效果
本文关键字:边缘检测 应用 图像 Windows8 应用程序 相机 | 更新日期: 2023-09-27 18:28:51
我正试图在Windows 8应用程序中直接在相机订阅源上应用图像处理效果。我尝试过一种方法,使用画布,并在应用效果后重新绘制图像,直接从网络摄像头获得。但这种方法适用于基本效果,但对于边缘检测等效果,它在使用画布方法时会产生很大的滞后和闪烁。
另一种方法是创建MFT(媒体基础转换),但它可以在C中实现,对此我一无所知。
有人能告诉我,我如何在Windows 8 metro风格的应用程序中直接在网络摄像头流上应用效果,通过改进画布方法,使边缘检测等大型效果不会出现任何问题,或者我如何在C#中应用MFT,因为我使用过C#语言,或者通过其他方法?
上周我刚刚在这个领域打了很多比赛,甚至考虑写一篇关于它的博客文章。我想这个答案也一样好。
您可以采用MFT方式,这需要在C++中完成,但您需要编写的内容在C#和C++之间没有太大区别。唯一需要注意的是,我认为MFT在YUV颜色空间中工作,因此您典型的卷积滤波器/效果可能会有点不同,或者需要转换为RGB。如果你决定走这条路,在C#应用程序端,你唯一需要做的就是调用MediaCapture.AddEffectAsync()。好吧,你需要编辑你的Package.appxmanifest等,但让我们先做第一件事。
如果您使用网络摄像头示例查看媒体捕获,它已经满足了您的需求。它将灰度效果应用于您的相机馈送。它包括一个在C#版本的应用程序中使用的C++MFT项目。我不得不将效果应用于MediaElement,这可能不是你所需要的,但也很简单——调用MediaElement.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,amp;v)。对于其他像素,将复制原始帧中的值。
TransformChroma使用m_transform变换像素。如果你想改变效果——你可以简单地改变m_transform矩阵,或者如果你需要像边缘检测过滤器一样访问相邻的像素——修改TransformImage_方法来处理这些像素。
这是一种方法。我认为这相当占用CPU,所以我个人更喜欢为这样的操作编写像素着色器。如何将像素着色器应用于视频流?好吧,我还没有完全做到,但我相信你可以很容易地将视频帧传输到DirectX表面,并在稍后调用它们上的像素着色器。到目前为止,我已经能够传输视频帧,我希望下周应用着色器。我可能会写一篇关于它的博客文章。我从媒体引擎原生C++播放示例中提取了meplayer类,并将其移动到转换为WinRTComponent库的模板C++DirectX项目中,然后将其与C#/XAML应用程序一起使用,将meplayer类别创建的swapchain与我在C#项目中用于显示视频的SwapChainBackgroundPanel关联起来。我不得不在甲氧麻黄酮类中做一些改变。首先,我必须将它移到一个公共名称空间,以便其他程序集可以使用它。然后,我不得不将它创建的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;
最后,我没有调用CreateSwapChainForCoreWindow,而是调用CreateSwapChainForComposition,并将swapchain与我的SwapChainPackgroundPanel关联起来:
// 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())
);
*EDIT跟随
忘了一件事。如果你的目标是保持纯C#——如果你想知道如何将帧捕获到WriteableBitmap(可能通过用MemoryStream调用MediaCapturePhotoToStreamAsync(),然后在流上调用WriteableBitmap.Source())——你可以使用WriteableBitmapEx来处理你的图像。它可能不是顶级性能,但如果你的分辨率不太高或帧速率要求不高,它可能就足够了。CodePlex上的项目还没有正式支持WinRT,但我有一个版本应该可以工作,你可以在这里尝试(Dropbox)。
据我所知,MFT需要在C++中实现。我相信有一个媒体转换SDK示例,它展示了从metro风格的应用程序实现一些简单的转换。