技术文章

在Raspberry Pi和NVIDIA Jetson上运行MATLAB图像处理算法

作者 Jim Brock and Murat Belge, MathWorks


得益于Raspberry Pi™等低成本硬件平台,现在比以往更容易在硬件上对图像处理算法进行原型验证。大多数图像处理算法需要进行大量计算,在嵌入式平台上以可接受的帧频运行它们非常困难。尽管Raspberry Pi在运行简单的图像处理算法时游刃有余,但大图像和复杂算法应在NVIDIA®Jetson等功能强大的硬件上运行。

本文将以色度键控效果为例,介绍在嵌入式硬件上部署MATLAB图像处理算法的简单工作流。我们将使用MATLAB Coder™通过算法生成C代码,然后使用在硬件上运行的实用程序在Raspberry Pi板卡上进行算法原型验证。最后,我们将算法移植至NVIDIA Jetson Tx1平台以保证实时性能。

本例中使用的代码可供下载

色度键控算法

色度键控广泛用于电视天气预报、电影制作和图片编辑应用程序,它是一种视频处理技术,首先针对单色背景(如绿色屏幕)拍摄前景对象,然后用不同场景(图1)替换该背景。

图1. 应用色度键控之前和之后的示例。

图1. 应用色度键控之前和之后的示例。

色度键控算法将图像中的每个像素与代表单背景色的基准色进行对比。如果像素颜色与基准色足够接近,像素将由之前所选场景图像的对应像素替换。从数学上讲,色度键控算法采用以下公式:

\[P_{final}(j,k)=m(j,k)*P_{original}(j,k)+(1-m(j,k))*P_{scene}(j,k)\]

其中 \(P_{final}(j,k)\) 代表进行色度键控后位置 \((j,k)\) 处的最终像素值,\(P_{original}(j,k)\) 是与原始图像对应的像素值,\(P_{scene}(j,k)\) 是代表替换单一背景色的场景的像素值,\(m(j,k)∈[0,1]\) 是掩码值。掩码数值 \(m(j,k)\) 对于前景像素应该为1,对于背景像素应该为0。0与1之间的掩码值可提供从背景到前景的平滑过渡。

每个像素的掩码值通常在YcbCr颜色空间而不是普通的RGB颜色空间中进行计算。YcbCr图像的Y分量代表亮度分量,用于确定图像的明暗度。Cb和Cr分量代表色度分量,用于衡量与基准色的相似度。由于仅使用图像的Cb和Cr分量衡量颜色相似度,该算法在应对单一背景色明暗区域的亮度值差异时十分稳健。

为衡量像素色与基准色的相似性,我们在色度空间中使用欧氏距离平方:

\[d^2 (j,k)=(Cb(j,k)-Cb_{ref} (j,k))^2+(Cr(j,k)-Cr_{ref} (j,k))^2\]

最后,使用以下公式计算图像中位置 \((j,k)\) 的掩码值:

\[m(j,k)=\left\{\begin{matrix}
1 & if d(j,k)>t_2 \\
0 & if d(j,k)<t_1 \\
\frac{d^2 (j,k)-t_1^2}{t_2^2-t_1^2} &  if t_1<d(j,k)<t_2
\end{matrix}\right.\]

其中 \(t_{1}\) 和 \(t_{2}\) \((t_{2} > t_{1}\)) 代表要确定的阈值。

MATLAB实现

以下是色度控键算法的MATLAB®实现。

function Pfinal = chromaKey(P, Pscene, refColorYCbCr, t1, t2)
Cbref = double(refColorYCbCr(1,1,2));
Crref = double(refColorYCbCr(1,1,3));
PYCbCr = rgb2ycbcr(P);
Cb = double(PYCbCr(:,:,2));
Cr = double(PYCbCr(:,:,3));
d = (Cb - Cbref).^2 + (Cr - Crref).^2;
t1 = t1^2;
t2 = t2^2;
m = zeros([size(d,1) size(d,2)]);
for j = 1:size(m,1)
    for k = 1:size(m,2)
        if d(j,k) > t2
            m(j,k) = 1;
        elseif d(j,k) > t1
            m(j,k) = (d(j,k) - t1) / (t2 - t1);
        end
    end
end
m = repmat(imgaussfilt(m,0.8), [1 1 3]);
Pfinal = uint8(double(P).*m + double(Pscene).*(1-m));
end

在MATLAB中,图像由类型uint8的[N, M, 3]数组表示。这表示,在执行数学操作前,我们需要将图像数据类型转换为“double”。为避免背景到前景的快速突变,我们对算出的掩模采用高斯过滤器进行滤波。

确定基准色和阈值

色度键控算法需要基准色和阈值。使用MATLAB Raspberry Pi支持包中的相机接口,我们拍摄了实际场景的图像。随后,我们可以凭借经验确定背景的适用基准色和相应的阈值。

r = raspi;
cam = cameraboard;
for k = 1:10
    img = snapshot(cam);
end

img = snapshot(cam); 命令绘制MATLAB中Raspberry Pi相机拍摄的图像。我们使用MATLAB绘制中的Data Cursor工具指定背景颜色(图2)。

图2. MATLAB中用于确定背景色值的Data Cursor工具。

图2. MATLAB中用于确定背景色值的Data Cursor工具。

要确定阈值,我们将循环运行算法并调整阈值:

refColorRGB = zeros([1,1,3],'uint8');
refColorRGB(1,1,:) = uint8([93 177 21]);
refColorYCbCr = rgb2ycbcr(refColorRGB);
t1 = 28;
t2 = 29;
data = coder.load('background.mat','bg');
scene = data.bg;
% Main loop
for k = 1:1000
   img = snapshot(cam);
   imgFinal = chromaKey(img, scene, refColorYCbCr, t1, t2);
   figure(1),image(img);
   figure(2),image(imgFinal);
   drawnow;
end

当运行代码时,我们会获得带有所选背景的图像(图3)。

图3. 左:原始图像。右:运行色度控键算法后获得的图像。

图3. 左:原始图像。右:运行色度控键算法后获得的图像。

将色度键控算法部署到Raspberry Pi

在部署代码前,我们需要编写一段包含有色度键控算法的循环体,循环体中还包括从相机拍摄图像,并在Raspberry Pi连接的显示器上显示:

function chromaKeyApp()
    %Chroma keying example for Raspberry Pi hardware.
    %#codegen
    % Copyright 2017 The MathWorks, Inc.
    w = matlab.raspi.webcam(0,[1280,720]);
    d = matlab.raspi.SDLVideoDisplay;
 
    refColorYCbCr = zeros([1,1,3],'uint8');
    refColorYCbCr(1,1,:) = uint8([0 76 98]);
    data = coder.load('background.mat','bg');
    scene = imrotate(data.bg,90);
    % Main loop
    for k = 1:60
        img = snapshot(w);
        img = chromaKey(img, scene, refColorYCbCr, 28, 29);
        displayImage(d,img);
    end
    release(w);
    release(d);
end

matlab.raspi.webcammatlab.raspi.SDLVideoDisplay在硬件上运行 实用程序的System objects™,它们可简化部署工作流程中相机的使用和Raspberry Pi显示。要编译并运行代码,我们可执行以下命令:

runOnHardware(r,'chromaKeyApp')

runOnHardware 功能会为Raspberry Pi硬件创建MATLAB Coder配置,为 chromaKeyApp.m 生成代码并对其部署。为了以合理的帧频运行算法,图像大小可缩小到640x480或320x240。

生成GPU代码

算法在Raspberry Pi上运行,但是它不会实现我们需要的实时性能。为了加快算法的速度,我们要使用GPU Coder™将其部署到NVIDIA Jetson平台。我们需要生成GPU代码以利用算法中的内在并行性。首先,我们会编写main函数进行封装,以利用OpenCV访问连接到NVIDIA Jetson的USB相机。此功能将视频帧从相机送到我们的chromaKey算法,随后在屏幕上显示输出内容。

生成GPU代码后,我们首先创建GPU Coder配置对象、设置GPU参数以指向NVIDIA Jetson硬件平台,然后加入自定义的main函数。我们不会在MATLAB主机上编译代码,因为我们将专门为NVIDIA Jetson平台生成代码。我们将创建脚本来设置GPU Coder配置、输入示例数据,并为我们的应用生成源代码。

% Create GPU Coder configuration for Jetson TX2
cfg = coder.gpuConfig('exe');
cfg.GpuConfig.MallocMode = 'Unified';
cfg.GpuConfig.ComputeCapability = '6.2';
cfg.GenCodeOnly = 1;
cfg.CustomSource = 'main_webcam.cu';
 
% Create sample inputs
fg = imread('greenScreenFrame.jpg');
bg = imread('Scenery.jpg');
refColorRGB = [70 130 85]; % RGB light Green
tmpColor = zeros([1,1,3],('uint8');
tmpColor(1,1,:) = uint8(refColorRGB);
refColor = rgb2ycbcr(tmpColor);
threshold1 = 14;
threshold2 = 20;

% Generate CUDA code for chromaKey
codegen -config cfg -args {fg,bg,refColor,threshold1,threshold2} chromaKey

我们随后在MATLAB中运行脚本为chromaKey算法生成CUDA代码。

向NVIDIA Jetson部署绿屏算法

要向NVIDIA Jetson部署生成的代码,我们需要使用以下MATLAB命令将全部所需文件打包到codegen目录。

% Prepare files for transfer to NVIDIA Jetson TX2
copyfile('Scenery.jpg','codegen/exe/chromaKey/');
copyfile('main_webcam.cu','codegen/exe/chromaKey/');
copyfile(fullfile(matlabroot,'extern','include','tmwtypes.h'),'codegen/exe/chromaKey/');
copyfile('buildAndRun.sh','codegen/exe/chromaKey/');

接下来,将生成的所有codegen文件夹从主机复制到NVIDIA Jetson控制板。在传输文件后,我们直接登录到NVIDIA Jetson以构建并运行应用程序。

登录到NVIDIA Jetson后,我们运行NVIDIA提供的jetson_clocks.sh脚本以将平台性能最大化、切换到包含生成的源代码(之前传输)的codegen目录,然后执行以下显示的编译命令。

在可执行程序(chromaKey)Build后,使用NVIDIA Jetson控制台上的USB网络摄像头通过以下命令运行应用程序。每秒显示帧数将显示在输出窗口中。

$> sudo ./jetson_clocks.sh
$> cd codegen/exe/chromaKey
$> nvcc -o chromaKey *.cu -rdc=true -arch sm_62 -O3 `pkg-config --cflags --libs opencv` -lcudart
$> ./chromaKey 1

图4显示出现绿屏效果前后NVIDIA Jetson板卡的USB相机的输出内容。

图4. 应用绿屏效果前后的示例。

图4. 应用绿屏效果前后的示例。

Raspberry Pi和NVIDIA Jetson性能对比

NVIDIA Jetson上GPU更加强大的并行处理能力极大地提高了算法的性能。Raspberry Pi实现了约1帧/秒的帧频,而NVIDIA Jetson对于1280x720大小的图像实现了20帧/秒以上的帧频。也就是说,无需对算法进行任何修改或优化,我们便获得了20倍以上的加速。通过优化MATLAB算法提高GPU代码的生成效率,我们可以实现更高的性能。

总结

在本例中,我们了解了如何快速生成MATLAB算法的代码并将其部署到Raspberry Pi等嵌入式硬件。我们快速确定了算法运行正确且需要进行并行化。使用MATLAB和GPU Coder,我们得以生成高度并行的算法实施方法并将其部署到NVIDIA Jetson板卡,实现了显著的性能改进。

2018年发布

查看文章,了解相关功能