Main Content

本页翻译不是最新的。点击此处可查看最新英文版本。

在应用程序中使用示例 C 主函数

此示例说明如何从 MATLAB® 代码编译 C 可执行文件,该代码实现一个简单的 Sobel 滤波器以对图像执行边缘检测。该可执行文件从磁盘读取一个图像,应用 Sobel 滤波算法,然后保存修改后的图像。

此示例说明如何生成和修改可在编译可执行文件时使用的示例主函数。

前提条件

要完成此示例,请安装以下产品:

  • MATLAB

  • MATLAB Coder™

  • C 编译器(对于大多数平台,MATLAB 随附提供了一个默认 C 编译器)。要查看支持的编译器列表,请参阅支持的编译器

    可以使用 mex -setup 更改默认编译器。请参阅更改默认编译器

教程文件:Sobel 滤波器

打开此示例以获取本教程的文件:sobel.mhello.jpg

教程文件的描述

您在此示例中使用的文件包含:

文件名文件类型描述
sobel.m函数代码Sobel 滤波算法的 MATLAB 实现。sobel.m 接受图像(表示为双精度矩阵)和阈值作为输入。该算法检测图像中的边缘(基于阈值)。sobel.m 返回修改后且显示边缘的图像。
hello.jpg图像文件Sobel 滤波器修改的图像。

 sobel.m 文件的内容

 hello.jpg 的内容

对图像运行 Sobel 滤波器

  1. 将原始图像读入 MATLAB 矩阵并显示该图像。

    im = imread('hello.jpg');
  2. 显示该图像以便与 Sobel 滤波器结果进行比较。

    image(im);

  3. Sobel 滤波算法对灰度图像进行运算。使用归一化值(0.0 代表黑色、1.0 代表白色),将彩色图像转换为等效的灰度图像。

    gray = (0.2989 * double(im(:,:,1)) + 0.5870 * double(im(:,:,2)) + 0.1140 * double(im(:,:,3)))/255;
  4. 要运行针对 Sobel 滤波器的 MATLAB 函数,请将灰度图像矩阵 gray 和阈值传递给函数 sobel。此示例使用 0.7 作为阈值。

    edgeIm = sobel(gray, 0.7);
  5. 要显示修改后的图像,请使用 repmat 函数重新格式化矩阵 edgeIm,以便将其传递给 image 命令。

    im3 = repmat(edgeIm, [1 1 3]);
    image(im3);

生成并测试 MEX 函数

  1. 要测试生成的代码是否在功能上等效于原始 MATLAB 代码并且不会发生运行时错误,请生成一个 MEX 函数。

    codegen -report sobel

    codegen 在当前工作文件夹中生成名为 sobel_mex 的 MEX 函数。

  2. 要运行针对 Sobel 滤波器的 MEX 函数,请将灰度图像矩阵 gray 和阈值传递给函数 sobel_mex。此示例使用 0.7 作为阈值。

    edgeImMex = sobel_mex(gray, 0.7);
  3. 要显示修改后的图像,请使用 repmat 函数重新格式化矩阵 edgeImMex,以便将其传递给 image 命令。

    im3Mex = repmat(edgeImMex, [1 1 3]);
    image(im3Mex);

    此图像与使用 MATLAB 函数创建的图像相同。

为 sobel.m 生成示例主函数

虽然您可以针对您的应用程序编写自定义主函数,但示例主函数提供了一个模板来帮助您合并生成的代码。

要为 Sobel 滤波器生成示例主函数,请执行下列操作:

  1. 为 C 静态库创建一个配置对象。

    cfg = coder.config('lib');

    对于 C/C++ 源代码、静态库、动态库和可执行文件的配置对象,GenerateExampleMain 的设置可控制示例主函数的生成。默认情况下,该设置设为 'GenerateCodeOnly',即生成示例主函数但不编译它。对于此示例,请勿更改 GenerateExampleMain 设置的值。

  2. 使用该配置对象生成一个 C 静态库。

    codegen -report -config cfg sobel

生成的静态库文件位于文件夹 codegen/lib/sobel 中。示例主文件位于子文件夹 codegen/lib/sobel/examples 中。

 示例主文件 main.c 的内容

复制示例主文件

不要修改 examples 子文件夹中的 main.cmain.h 文件。如果修改这些文件,则当您重新生成代码时,MATLAB Coder 不会重新生成示例主文件。它会发出警告,提示检测到生成文件发生了更改。

将文件 main.cmain.h 从文件夹 codegen/lib/sobel/examples 复制到另一个位置。对于此示例,请将文件复制到当前工作文件夹中。修改新位置中的文件。

修改生成的示例主函数

示例主函数声明数据(包括动态分配的数据)并将数据初始化为零值。它调用入口函数(其参数设置为零值),但它不使用从入口函数返回的值。

C 主函数必须满足您的应用程序的要求。此示例修改示例主函数以满足 Sobel 滤波器应用程序的要求。

此示例修改文件 main.c 以便 Sobel 滤波器应用程序:

  • 从二进制文件中读入灰度图像。

  • 应用 Sobel 滤波算法。

  • 将修改后的图像保存为二进制文件。

修改 main 函数

main 函数修改为:

  • 接受包含灰度图像数据的文件和阈值作为输入参数。

  • 使用灰度图像数据流的地址和阈值作为输入参数调用函数 main_sobel

在函数 main 中:

  1. 删除 (void)argc(void)argv 声明。

  2. 声明变量 filename 以保存包含灰度图像数据的二进制文件的名称。

    const char *filename;
  3. 声明变量 threshold 以保存阈值。

    double threshold;
  4. 声明变量 fd 以保存应用程序从 filename 读入的灰度图像数据的地址。

    FILE *fd;
  5. 添加一个用于检查三个参数的 if 语句。

    if (argc != 3) {
          printf("Expected 2 arguments: filename and threshold\n");
          exit(-1);
    }
  6. 将包含灰度图像数据的文件的输入参数 argv[1] 赋给 filename

    filename = argv[1];
  7. 将阈值的输入参数 argv[2] 赋给 threshold,从而将输入从字符串转换为双精度数值。

    threshold = atof(argv[2]);
  8. 打开包含其名称在 filename 中指定的灰度图像数据的文件。将数据流的地址赋给 fd

    fd = fopen(filename, "rb");
  9. 要验证可执行文件可以打开 filename,请编写一个 if 语句,其功能是在 fd 的值为 NULL 时退出程序。

    if (fd == NULL) {
       exit(-1);
    }
  10. 将对 main_sobel 的函数调用替换为带输入参数 fdthreshold 调用 main_sobel

    main_sobel(fd, threshold);
  11. 调用 sobel_terminate 后关闭灰度图像文件。

    fclose(fd);

 修改后的 main 函数

修改初始化函数 argInit_d1024xd1024_real_T

在示例主文件中,函数 argInit_d1024xd1024_real_T 为传递给 Sobel 滤波器的图像创建一个动态分配的可变大小数组 (emxArray)。此函数将 emxArray 初始化为默认大小,并将 emxArray 的元素初始化为 0。它返回初始化后的 emxArray。

对于 Sobel 滤波器应用程序,请修改该函数以将灰度图像数据从二进制文件读入 emxArray 中。

在函数 argInit_d1024xd1024_real_T 中:

  1. 将输入参数 void 替换为参数 FILE *fd。此变量指向该函数读入的灰度图像数据。

    static emxArray_real_T *argInit_d1024xd1024_real_T(FILE *fd)
  2. 更改变量 idx0idx1 的值,以匹配灰度图像矩阵 gray 的维度。这些变量保留 argInit_d1024xd1024_real_T 创建的 emxArray 的维度的大小值。

    int idx0 = 484;
    int idx1 = 648;

    MATLAB 以列优先格式存储矩阵数据,而 C 以行优先格式存储矩阵数据。请相应地声明维度。

  3. emxCreate_real_T 函数的值更改为图像大小。

    result = emxCreate_real_T(484, 648);
  4. 定义变量 element 以保存从灰度图像数据读入的值。

    double element;
  5. 通过向内部 for 循环添加 fread 命令,更改 for 循环构造以将数据点从归一化图像读入 element 中。

    fread(&element, 1, sizeof(element), fd);
  6. for 循环内,将 element 指定为 emxArray 数据的值集合。

    result->data[idx0 + result->size[0] * idx1] = element;

 修改后的初始化函数 argInit_d1024xd1024_real_T

编写 saveImage 函数

MATLAB 函数 sobel.m 与 MATLAB 数组交互,而 Sobel 滤波器应用程序与二进制文件交互。

要将用 Sobel 滤波算法修改后的图像保存为二进制文件,请创建函数 saveImage。函数 saveImage 将来自 emxArray 的数据写入二进制文件。它使用的构造类似于函数 argInit_d1024xd1024_real_T 所使用的构造。

在文件 main.c 中:

  1. 定义函数 saveImage,它将 emxArray edgeImage 的地址作为输入,输出类型为 void。

    static void saveImage(emxArray_uint8_T *edgeImage)
    {
    }
  2. 定义变量 idx0idx1,就像在函数 argInit_d1024xd1024_real_T 中定义它们一样。

    int idx;
    int idx1;
  3. 定义变量 element 以存储从 emxArray 读取的数据。

    uint8_T element;
  4. 打开二进制文件 edge.bin 以写入修改后的图像。将 edge.bin 的地址赋给 FILE *fd

    FILE *fd = fopen("edge.bin", "wb");
  5. 要验证可执行文件可以打开 edge.bin,请编写一个 if 语句,其功能是在 fd 的值为 NULL 时退出程序。

    if (fd == NULL) {
       exit(-1);
    }
  6. 编写一个嵌套的 for 循环构造,该构造类似于函数 argInit_d1024xd1024_real_T 中的构造。

    for (idx0 = 0; idx0 < edgeImage->size[0U]; idx0++)
    {
        for (idx1 = 0; idx1 < edgeImage->size[1U]; idx1++)
        {
        }
    }
  7. 在内部 for 循环内,将来自修改后的图像数据的值赋给 element

    element = edgeImage->data[idx0 + edgeImage->size[0] * idx1];
  8. 在为 element 赋值后,将来自 element 的值写入文件 edge.bin

    fwrite(&element, 1, sizeof(element), fd);
  9. for 循环构造后,关闭 fd

    fclose(fd);

 saveImage 函数

修改 main_sobel 函数

在示例主函数中,函数 main_sobel 为灰度图像和修改后的图像的数据创建 emxArray。它调用函数 argInit_d1024xd1024_real_T 以初始化灰度图像的 emxArray。main_sobel 将 emxArray 和初始化函数 argInit_real_T 返回的阈值 0 传递给函数 sobel。当函数 main_sobel 结束时,它会丢弃函数 sobel 的结果。

对于 Sobel 滤波器应用程序,请将函数 main_sobel 修改为:

  • 将灰度图像数据的地址和阈值作为输入。

  • 使用 argInit_d1024xd1024_real_T 从地址读取数据。

  • 使用阈值 threshold 将数据传递给 Sobel 滤波算法。

  • 使用 saveImage 保存结果。

在函数 main_sobel 中:

  1. 将函数的输入参数替换为 FILE *fddouble threshold 参数。

    static void main_sobel(FILE *fd, double threshold)
  2. 将输入参数 fd 传递给 argInit_d1024xd1024_real_T 的函数调用。

    originalImage = argInit_d1024xd1024_real_T(fd);
  3. 将对 sobel 的函数调用中的阈值输入替换为 threshold

    sobel(originalImage, threshold, edgeImage);
  4. 调用函数 sobel 后,带 edgeImage 输入调用函数 saveImage

    saveImage(edgeImage);

 修改后的 main_sobel 函数

修改函数声明

为了匹配您对函数定义所做的更改,请对函数声明进行以下更改:

  1. 将函数 *argInit_d1024xd1024_real_T 的输入更改为 FILE *fd

    static emxArray_real_T *argInit_d1024xd1024_real_T(FILE *fd);
  2. 将函数 main_sobel 的输入更改为 FILE *fddouble threshold

    static void main_sobel(FILE *fd, double threshold);
  3. 添加函数 saveImage

    static void saveImage(emxArray_uint8_T *edgeImage);

 修改后的函数声明

修改包含文件

对于在 main.c 中使用的输入/输出函数,将头文件 stdio.h 添加到包含的文件列表中。

#include <stdio.h>

 修改后的包含文件

修改后的 main.c 文件的内容

 main.c

生成 Sobel 滤波器应用程序

  1. 导航到工作文件夹(如果当前不在该文件夹中)。

  2. 为 C 独立可执行文件创建一个配置对象。

    cfg = coder.config('exe');
  3. 使用该配置对象和修改后的主函数为 Sobel 滤波器生成 C 独立可执行文件。

    codegen -report -config cfg sobel main.c main.h

默认情况下,如果在 Windows® 平台上运行 MATLAB,则会在当前工作文件夹中生成可执行文件 sobel.exe。如果在 Windows 以外的平台上运行 MATLAB,则文件扩展名是该平台的对应扩展名。默认情况下,为可执行文件生成的代码位于文件夹 codegen/exe/sobel 中。

运行 Sobel 滤波器应用程序

  1. 创建 MATLAB 矩阵 gray(如果它当前不在 MATLAB 工作区中):

    im = imread('hello.jpg');
    gray = (0.2989 * double(im(:,:,1)) + 0.5870 * double(im(:,:,2)) + 0.1140 * double(im(:,:,3)))/255;
  2. 使用 fopenfwrite 命令将矩阵 gray 写入一个二进制文件。应用程序读入此二进制文件。

    fid = fopen('gray.bin', 'w');
    fwrite(fid, gray, 'double');
    fclose(fid);
  3. 运行可执行文件,将文件 gray.bin 和阈值 0.7 传递给它。

    要在 Windows 平台上运行 MATLAB 中的示例,请执行以下命令:

    system('sobel.exe gray.bin 0.7');

    可执行文件会生成 edge.bin 文件。

显示生成的图像

  1. 使用 fopenfread 命令将文件 edge.bin 读入 MATLAB 矩阵 edgeImExe

    fd = fopen('edge.bin', 'r');
    edgeImExe = fread(fd, size(gray), 'uint8');
    fclose(fd);
  2. 将矩阵 edgeImExe 传递给函数 repmat 并显示图像。

    im3Exe = repmat(edgeImExe, [1 1 3]);
    image(im3Exe);

    图像与来自 MATLAB 和 MEX 函数的图像匹配。

相关主题