在应用程序中使用示例 C 主函数
此示例说明如何从 MATLAB® 代码编译 C 可执行文件,该代码实现一个简单的索贝尔滤波器以对图像执行边缘检测。该可执行文件从磁盘读取一个图像,应用索贝尔滤波算法,然后保存修改后的图像。
此示例说明如何生成和修改可在编译可执行文件时使用的示例主函数。
前提条件
要完成此示例,请安装以下产品:
教程文件:索贝尔滤波器
打开此示例以获取本教程的文件:sobel.m
和 hello.jpg
。
教程文件的描述
您在此示例中使用的文件包含:
文件名 | 文件类型 | 描述 |
---|---|---|
sobel.m | 函数代码 | 索贝尔滤波算法的 MATLAB 实现。sobel.m 接受图像(表示为双精度矩阵)和阈值作为输入。该算法检测图像中的边缘(基于阈值)。sobel.m 返回修改后且显示边缘的图像。 |
hello.jpg | 图像文件 | 索贝尔滤波器修改的图像。 |
对图像运行索贝尔滤波器
将原始图像读入 MATLAB 矩阵并显示该图像。
im = imread('hello.jpg');
显示该图像以便与索贝尔滤波器结果进行比较。
image(im);
索贝尔滤波算法对灰度图像进行运算。使用归一化值(0.0 代表黑色、1.0 代表白色),将彩色图像转换为等效的灰度图像。
gray = (0.2989 * double(im(:,:,1)) + 0.5870 * double(im(:,:,2)) + 0.1140 * double(im(:,:,3)))/255;
要运行针对索贝尔滤波器的 MATLAB 函数,请将灰度图像矩阵
gray
和阈值传递给函数sobel
。此示例使用 0.7 作为阈值。edgeIm = sobel(gray, 0.7);
要显示修改后的图像,请使用
repmat
函数重新格式化矩阵edgeIm
,以便将其传递给image
命令。im3 = repmat(edgeIm, [1 1 3]); image(im3);
生成并测试 MEX 函数
要测试生成的代码是否在功能上等效于原始 MATLAB 代码并且不会发生运行时错误,请生成一个 MEX 函数。
codegen -report sobel
codegen
在当前工作文件夹中生成名为sobel_mex
的 MEX 函数。要运行针对索贝尔滤波器的 MEX 函数,请将灰度图像矩阵
gray
和阈值传递给函数sobel_mex
。此示例使用 0.7 作为阈值。edgeImMex = sobel_mex(gray, 0.7);
要显示修改后的图像,请使用
repmat
函数重新格式化矩阵edgeImMex
,以便将其传递给image
命令。im3Mex = repmat(edgeImMex, [1 1 3]); image(im3Mex);
此图像与使用 MATLAB 函数创建的图像相同。
为 sobel.m 生成示例主函数
虽然您可以针对您的应用程序编写自定义主函数,但示例主函数提供了一个模板来帮助您合并生成的代码。
要为索贝尔滤波器生成示例主函数,请执行下列操作:
为 C 静态库创建一个配置对象。
cfg = coder.config('lib');
对于 C/C++ 源代码、静态库、动态库和可执行文件的配置对象,
GenerateExampleMain
的设置可控制示例主函数的生成。默认情况下,该设置设为'GenerateCodeOnly'
,即生成示例主函数但不编译它。对于此示例,请勿更改GenerateExampleMain
设置的值。使用该配置对象生成一个 C 静态库。
codegen -report -config cfg sobel
生成的静态库文件位于文件夹 codegen/lib/sobel
中。示例主文件位于子文件夹 codegen/lib/sobel/examples
中。
复制示例主文件
不要修改 examples
子文件夹中的 main.c
和 main.h
文件。如果修改这些文件,则当您重新生成代码时,MATLAB Coder 不会重新生成示例主文件。它会发出警告,提示检测到生成文件发生了更改。
将文件 main.c
和 main.h
从文件夹 codegen/lib/sobel/examples
复制到另一个位置。对于此示例,请将文件复制到当前工作文件夹中。修改新位置中的文件。
修改生成的示例主函数
示例主函数声明数据(包括动态分配的数据)并将数据初始化为零值。它调用入口函数(其参量设置为零值),但它不使用从入口函数返回的值。
C 主函数必须满足您的应用程序的要求。此示例修改示例主函数以满足索贝尔滤波器应用程序的要求。
此示例修改文件 main.c
以便索贝尔滤波器应用程序:
从二进制文件中读入灰度图像。
应用索贝尔滤波算法。
将修改后的图像保存为二进制文件。
修改 main 函数
将 main
函数修改为:
接受包含灰度图像数据的文件和阈值作为输入参量。
使用灰度图像数据流的地址和阈值作为输入参量调用函数
main_sobel
。
在函数 main
中:
删除
(void)argc
和(void)argv
声明。声明变量
filename
以保存包含灰度图像数据的二进制文件的名称。const char *filename;
声明变量
threshold
以保存阈值。double threshold;
声明变量
fd
以保存应用程序从filename
读入的灰度图像数据的地址。FILE *fd;
添加一个用于检查三个参量的
if
语句。if (argc != 3) { printf("Expected 2 arguments: filename and threshold\n"); exit(-1); }
将包含灰度图像数据的文件的输入参量
argv[1]
赋给filename
。filename = argv[1];
将阈值的输入参量
argv[2]
赋给threshold
,从而将输入从字符串转换为双精度数值。threshold = atof(argv[2]);
打开包含其名称在
filename
中指定的灰度图像数据的文件。将数据流的地址赋给fd
。fd = fopen(filename, "rb");
要验证可执行文件可以打开
filename
,请编写一个if
语句,其功能是在fd
的值为NULL
时退出程序。if (fd == NULL) { exit(-1); }
将对
main_sobel
的函数调用替换为带输入参量fd
和threshold
调用main_sobel
。main_sobel(fd, threshold);
调用
sobel_terminate
后关闭灰度图像文件。fclose(fd);
修改初始化函数 argInit_d1024xd1024_real_T
在示例主文件中,函数 argInit_d1024xd1024_real_T
为传递给索贝尔滤波器的图像创建一个动态分配的可变大小数组 (emxArray)。此函数将 emxArray 初始化为默认大小,并将 emxArray 的元素初始化为 0。它返回初始化后的 emxArray。
对于索贝尔滤波器应用程序,请修改该函数以将灰度图像数据从二进制文件读入 emxArray 中。
在函数 argInit_d1024xd1024_real_T
中:
将输入参量
void
替换为参量FILE *fd
。此变量指向该函数读入的灰度图像数据。static emxArray_real_T *argInit_d1024xd1024_real_T(FILE *fd)
更改变量
idx0
和idx1
的值,以匹配灰度图像矩阵gray
的维度。这些变量保留argInit_d1024xd1024_real_T
创建的 emxArray 的维度的大小值。int idx0 = 484; int idx1 = 648;
MATLAB 以列优先格式存储矩阵数据,而 C 以行优先格式存储矩阵数据。请相应地声明维度。
将
emxCreate_real_T
函数的值更改为图像大小。result = emxCreate_real_T(484, 648);
定义变量
element
以保存从灰度图像数据读入的值。double element;
通过向内部
for
循环添加fread
命令,更改for
循环构造以将数据点从归一化图像读入element
中。fread(&element, 1, sizeof(element), fd);
在
for
循环内,将element
指定为 emxArray 数据的值集合。result->data[idx0 + result->size[0] * idx1] = element;
编写 saveImage 函数
MATLAB 函数 sobel.m
与 MATLAB 数组交互,而索贝尔滤波器应用程序与二进制文件交互。
要将用索贝尔滤波算法修改后的图像保存为二进制文件,请创建函数 saveImage
。函数 saveImage
将来自 emxArray 的数据写入二进制文件。它使用的构造类似于函数 argInit_d1024xd1024_real_T
所使用的构造。
在文件 main.c
中:
定义函数
saveImage
,它将 emxArrayedgeImage
的地址作为输入,输出类型为 void。static void saveImage(emxArray_uint8_T *edgeImage) { }
定义变量
idx0
和idx1
,就像在函数argInit_d1024xd1024_real_T
中定义它们一样。int idx; int idx1;
定义变量
element
以存储从 emxArray 读取的数据。uint8_T element;
打开二进制文件
edge.bin
以写入修改后的图像。将edge.bin
的地址赋给FILE *fd
。FILE *fd = fopen("edge.bin", "wb");
要验证可执行文件可以打开
edge.bin
,请编写一个if
语句,其功能是在fd
的值为NULL
时退出程序。if (fd == NULL) { exit(-1); }
编写一个嵌套的
for
循环构造,该构造类似于函数argInit_d1024xd1024_real_T
中的构造。for (idx0 = 0; idx0 < edgeImage->size[0U]; idx0++) { for (idx1 = 0; idx1 < edgeImage->size[1U]; idx1++) { } }
在内部
for
循环内,将来自修改后的图像数据的值赋给element
。element = edgeImage->data[idx0 + edgeImage->size[0] * idx1];
在为
element
赋值后,将来自element
的值写入文件edge.bin
。fwrite(&element, 1, sizeof(element), fd);
在
for
循环构造后,关闭fd
。fclose(fd);
修改 main_sobel 函数
在示例主函数中,函数 main_sobel
为灰度图像和修改后的图像的数据创建 emxArray。它调用函数 argInit_d1024xd1024_real_T
以初始化灰度图像的 emxArray。main_sobel
将 emxArray 和初始化函数 argInit_real_T
返回的阈值 0 传递给函数 sobel
。当函数 main_sobel
结束时,它会丢弃函数 sobel
的结果。
对于索贝尔滤波器应用程序,请将函数 main_sobel
修改为:
将灰度图像数据的地址和阈值作为输入。
使用
argInit_d1024xd1024_real_T
从地址读取数据。使用阈值
threshold
将数据传递给索贝尔滤波算法。使用
saveImage
保存结果。
在函数 main_sobel
中:
将函数的输入参量替换为
FILE *fd
和double threshold
参量。static void main_sobel(FILE *fd, double threshold)
将输入参量
fd
传递给argInit_d1024xd1024_real_T
的函数调用。originalImage = argInit_d1024xd1024_real_T(fd);
将对
sobel
的函数调用中的阈值输入替换为threshold
。sobel(originalImage, threshold, edgeImage);
调用函数
sobel
后,带edgeImage
输入调用函数saveImage
。saveImage(edgeImage);
修改函数声明
为了匹配您对函数定义所做的更改,请对函数声明进行以下更改:
将函数
*argInit_d1024xd1024_real_T
的输入更改为FILE *fd
。static emxArray_real_T *argInit_d1024xd1024_real_T(FILE *fd);
将函数
main_sobel
的输入更改为FILE *fd
和double threshold
。static void main_sobel(FILE *fd, double threshold);
添加函数
saveImage
。static void saveImage(emxArray_uint8_T *edgeImage);
修改包含文件
对于在 main.c
中使用的输入/输出函数,将头文件 stdio.h
添加到包含的文件列表中。
#include <stdio.h>
修改后的 main.c
文件的内容
生成索贝尔滤波器应用程序
导航到工作文件夹(如果当前不在该文件夹中)。
为 C 独立可执行文件创建一个配置对象。
cfg = coder.config('exe');
使用该配置对象和修改后的主函数为索贝尔滤波器生成 C 独立可执行文件。
codegen -report -config cfg sobel main.c main.h
默认情况下,如果在 Windows® 平台上运行 MATLAB,则会在当前工作文件夹中生成可执行文件 sobel.exe
。如果在 Windows 以外的平台上运行 MATLAB,则文件扩展名是该平台的对应扩展名。默认情况下,为可执行文件生成的代码位于文件夹 codegen/exe/sobel
中。
运行索贝尔滤波器应用程序
创建 MATLAB 矩阵
gray
(如果它当前不在 MATLAB 工作区中):im = imread('hello.jpg');
gray = (0.2989 * double(im(:,:,1)) + 0.5870 * double(im(:,:,2)) + 0.1140 * double(im(:,:,3)))/255;
使用
fopen
和fwrite
命令将矩阵gray
写入一个二进制文件。应用程序读入此二进制文件。fid = fopen('gray.bin', 'w'); fwrite(fid, gray, 'double'); fclose(fid);
运行可执行文件,将文件
gray.bin
和阈值 0.7 传递给它。要在 Windows 平台上运行 MATLAB 中的示例,请执行以下命令:
system('sobel.exe gray.bin 0.7');
可执行文件会生成
edge.bin
文件。
显示生成的图像
使用
fopen
和fread
命令将文件edge.bin
读入 MATLAB 矩阵edgeImExe
。fd = fopen('edge.bin', 'r'); edgeImExe = fread(fd, size(gray), 'uint8'); fclose(fd);
将矩阵
edgeImExe
传递给函数repmat
并显示图像。im3Exe = repmat(edgeImExe, [1 1 3]); image(im3Exe);
图像与来自 MATLAB 和 MEX 函数的图像匹配。