One-button MATLAB-to-C Conversion

In the past, converting MATLAB algorithms into C code required manual translation--a slow, error-prone process. Now, you can generate C automatically using Catalytic MCS. Here's how it works.


January 16, 2007
URL:http://www.drdobbs.com/cpp/one-button-matlab-to-c-conversion/196901296

[Editor's note: For a related product story, click here.]

Introduction
For signal processing algorithm developers, MATLAB from The MathWorks is an essential tool. Its vector semantics and powerful visualization capabilities are a necessity for algorithm development. However, since algorithm development is often separate from product development, MATLAB programmers generally need to create a C-code model of their algorithm. This C-code model of the algorithm can have a number uses, including:

Creating the C-code model requires developers to temporarily freeze algorithm changes and manually translate their algorithms into C. Manually translating the algorithmic model to C code can be a laborious task. As a result, developers tend to avoid making this translation until late in the design process. This increases the length of the development process and increases the cost of fixing bugs. Manual translation is also an error-prone process. This makes it difficult to determine if functional bugs are the result of a poor algorithm or a translation error.

These challenges can be addressed by using a tool such as Catalytic MCS that automatically generates C-code from MATLAB. This automation also enables developers to continue using MATLAB to modify their algorithms and generate a reference model as needed.

In this article, we describe how to use Catalytic MCS to greatly ease the task of integrating MATLAB algorithms into existing C-code. Throughout the article, we use a vertical image scaling algorithm as a running example.

Example: Vertical Image Scaler
For this example, we have developed a vertical image scaler in MATLAB. The scaler algorithm takes a single image and a scaling ratio as input and computes a scaled image, using a simple linear interpolation between pixel rows. Eventually, our goal is to integrate this algorithm with a video decoder to scale an input video stream to a variety of display types. For example, the algorithm could be used to scale an HDTV video (720 scan lines) to an NTSC video display (480 scan lines).

The video decoding application has already been developed using Microsoft's DirectShow, a C++-based environment for developing audio and video applications. We have created a skeleton DirectShow image transform filter, into which we will integrate the vertical scaling code. The resulting application will open an input video file, scale each frame, and play the resulting video stream on the screen.


Figure 1. Overview of Algorithm and Testbench in MATLAB and DirectShow
The Steps
Now we describe the steps for integrating the vertical image scaler algorithm into a DirectShow video application.

Step 1: Separate the algorithm from the testbench
MATLAB makes it easy to combine the algorithm and testbench into a single function or file. However, when integrating algorithms with other pieces of C-code, it's important to separate the algorithm from the testbench. This separation makes it easy to determine what the algorithm's inputs and outputs are. This also eases the task of integrating the algorithm into a larger application.

For the vertical scaler example, this means separating the actual scaling algorithm from the MATLAB code that reads in images and displays images. After this separation, we have a couple of M-code files:

Step 2: Define the types of the inputs
Now that the algorithm boundary is established, we define the types of the input variables. One of the powers of MATLAB is type polymorphism " the ability for MATLAB operations to have different semantics for different types. However, when integrating an algorithm developed in MATLAB into C-code, we must define a fixed-data-type interface to the C-code. In this process, the Catalytic MCS user defines the types of the input variables. Given this information, MCS automatically determines the types of the output variables. MCS provides a set of MATLAB functions that allow developers to define the types of the inputs. Users must define the dimensionality (scalar, row vector, column vector, 2D or 3D matrix, etc.) and basetype (real, complex, integer, logical, etc.) of a variable. If the specific sizes of the inputs are also known, they can also be defined, but this is not mandatory.

For the vertical scaler example, we defined the types of the following inputs:


(Click to enlarge)

Figure 2. Interfaces to vscalerrgb M-code and C-code

Step 3: Generate C-code
Generating C-code with MCS is easy. Once the inputs are defined, it's simply a matter of applying MCS to the MATLAB code. After opening the top-level algorithm MATLAB file in the Catalytic GUI, just press the "C" button. The generated C-code appears in the right hand pane, as shown in Figure 3.


(Click to enlarge)

Figure 3. The Catalytic GUI.

With the Catalytic GUI, users can "cross-probe" between the original MATLAB code and generated C-code. That is, they can select a line of MATLAB and have the GUI highlight the corresponding C-code and vice versa. In order to improve code readability, MCS uses the original MATLAB variable names in the C code. In addition, MCS can optionally embed the original MATLAB code and comments into the C code.


Figure 4. MCS reuses original variable names and comments

When executed on vscaler.m, Catalytic MCS generates the following files:

Users have a great deal of control over the C-code generated by MCS. For example, MCS can insert runtime checks into the code to flag conditions such as array overruns and division-by-zero. In the interest of speed, these checks can be disabled.Step 4: Test C-code
After the C-code has been generated, we test it with the original MATLAB testbench to ensure functional equivalence of the resulting C-code. To facilitate this process, Catalytic MCS has a feature to compile and link the generated C-code into a MATLAB function. This feature uses MATLAB's standard MEX API, which enables MATLAB functions to call functions written in other programming languages.

In this example, MCS replaces the original vscalerrgb.m file with a form of the C code compiled for MEX. This compiled code is called vscalerrgb.dll. We can now test the compiled code by simply calling the existing MATLAB testbench (test_vscaler.m). Our testbench now calls vscalerrgb.dll instead of vscalerrgb.m. This enables us to verify that the generated C-code behaves identically to the original algorithm without changing the testbench.

Step 5: Integrate C-code
We are now confident that the generated C-code is functionally identical to the source MATLAB code. The last step is to integrate it with the C-code for the rest of the application.

The video decoding application makes use of the DirectShow API. In a DirectShow application, a number of "filters" are linked together to implement the steps in decoding and displaying video:

DirectShow coordinates the interaction between filters and imposes a consistent memory management policy. Microsoft designed DirectShow specifically to allow easy incorporation of custom filters.

For this application, we create a custom filter to implement the vertical scaling algorithm. We start by deriving a filter from CTransformFilter, a C++ base class supplied by DirectShow. The CTransformFilter provides a way to access and modify samples in a media stream; in this context, a "sample" represents individual frames in a video stream. Frames are integer arrays, with each pixel represented by a 3-byte RGB tuple. Recall that vscalerrgb expects three integer matrices, representing R, G, and B values; the output of vscalerrgb has a similar format. In order to integrate vscalerrgb with the DirectShow application, we must implement a simple wrapper to translate the image frame as it passes in and out of vscalerrgb; the wrapper does nothing more than "demux" input frames into separate R, G, and B arrays just prior to entering vscalerrgb. The reverse operation is performed at the output of vscalerrgb.

It is worth noting that MATLAB stores 2-D matrices in column-major order. In other words, a column vector in MATLAB is stored as contiguous elements in memory. C, on the other hand, uses row-major ordering: a row vector is stored as contiguous elements in memory. This discrepancy in representation could lead to confusion whenever the orientation of 2-D data is critical to the application. Fortunately, MCS has the option to handle row-major data by generating code to transpose matrices as they pass in and out of MCS-generated C code. Thus, even though our MATLAB function operates on column-major data, we don't have to worry about manually transposing matrices in our C implementation.

In addition, MATLAB functions typically allocate memory for output variables dynamically, and MCS follows this convention. By default, MCS-generated code will allocate memory for the output R, G, and B matrices within vscalerrgb.c. We need to deallocate this memory in our wrapper code. Note that MCS also supports a directive, CATALYTIC_pass_by_ref(), which allows the user to pass pre-allocated output arrays into the MATLAB function. In some cases, this mode of function interface may be more appropriate for integration with other C code.

At this point, we can run our video decoding application and successfully demonstrate real-time vertical scaling of a video.


(Click to enlarge)

Figure 5 The CatalyticMCS filter in a DirectShow graph

Benefits
Automating a manual process has the immediate benefit of shortening design time. But automatically generating C-code from MATLAB has many more advantages for algorithm developers. They can continue to work in their preferred environment and generate reference models of their algorithm on demand. The generated C-code can be easily verified for correctness with the original MATLAB testbench. Algorithm execution is not limited to the MATLAB environment, but can now be run on any platform with an ANSI C compiler.

About the authors
Niraj Shah is a product marketing manager at Catalytic Inc. He recently received a Ph.D. in electrical engineering and computer sciences from the University of California at Berkeley. His research focused on programming models for application-specific processors. Previously, Niraj was a venture partner at ITU Ventures, an early-stage venture capital firm investing in companies emerging from leading research institutions. He can be reached at [email protected].

Robert Yu is an applications engineer at Catalytic Inc. Before joining Catalytic, he held software development and management positions at a variety of Silicon Valley companies. During this time, his work focused on signal processing and telecommunications applications. Robert holds bachelor's and master's degrees in electrical engineering from Cornell. He can be reached at [email protected].

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.