Contenu principal

La traduction de cette page n'est pas à jour. Cliquez ici pour voir la dernière version en anglais.

Générer du code pour détecter les contours sur les images

Cet exemple montre comment générer une bibliothèque C autonome à partir d’un code MATLAB® qui implémente un filtre de Sobel simple permettant de détecter les contours sur les images. Il montre également comment générer et tester une fonction MEX dans MATLAB avant de générer le code C pour vérifier que le code MATLAB convient pour la génération de code.

À propos de la fonction sobel

La fonction sobel.m reçoit une image (représentée par une matrice double) et une valeur de seuil et renvoie une image aux contours détectés (en fonction de la valeur de seuil).

type sobel
% edgeImage = sobel(originalImage, threshold)
% Sobel edge detection. Given a normalized image (with double values)
% return an image where the edges are detected w.r.t. threshold value.
function edgeImage = sobel(originalImage, threshold) %#codegen
assert(all(size(originalImage) <= [1024 1024]));
assert(isa(originalImage, 'double'));
assert(isa(threshold, 'double'));

k = [1 2 1; 0 0 0; -1 -2 -1];
H = conv2(double(originalImage),k, 'same');
V = conv2(double(originalImage),k','same');
E = sqrt(H.*H + V.*V);
edgeImage = uint8((E > threshold) * 255);

Générer la fonction MEX

Générez une fonction MEX avec la commande codegen.

codegen sobel
Code generation successful.

Avant de générer le code C, il est conseillé de tester la fonction MEX dans MATLAB pour vérifier que son fonctionnement est équivalent à celui du code MATLAB d’origine et qu’aucune erreur run-time ne se produit. Par défaut, codegen génère une fonction MEX nommée sobel_mex dans le dossier actuel. Vous pouvez ainsi tester le code MATLAB et la fonction MEX puis comparer les résultats.

Lire l’image d’origine

Utilisez la commande imread standard.

im = imread('hello.jpg');
image(im);

Figure contains an axes object. The axes object contains an object of type image.

Convertir l’image en niveaux de gris

Convertissez l’image couleur (présentée ci-dessus) en une image équivalente en niveaux de gris avec des valeurs normalisées (0.0 pour le noir et 1.0 pour le blanc).

gray = (0.2989 * double(im(:,:,1)) + 0.5870 * double(im(:,:,2)) + 0.1140 * double(im(:,:,3)))/255;

Exécuter la fonction MEX (le filtre de Sobel)

Passez l’image normalisée et une valeur de seuil.

edgeIm = sobel_mex(gray, 0.7);

Afficher le résultat

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

Figure contains an axes object. The axes object contains an object of type image.

Générer du code C autonome

codegen -config coder.config('lib') sobel
Code generation successful.

Utilisez codegen avec l’option -config coder.config('lib') pour générer une bibliothèque C autonome. Par défaut, le code généré pour la bibliothèque se trouve dans le dossier codegen/lib/sobel/.

Inspecter la fonction générée

type codegen/lib/sobel/sobel.c
/*
 * File: sobel.c
 *
 * MATLAB Coder version            : 25.2
 * C/C++ source code generated on  : 09-Aug-2025 10:02:00
 */

/* Include Files */
#include "sobel.h"
#include "conv2AXPYSameCMP.h"
#include "sobel_data.h"
#include "sobel_emxutil.h"
#include "sobel_initialize.h"
#include "sobel_types.h"
#include "sqrt.h"
#include "omp.h"
#include <emmintrin.h>

/* Function Declarations */
static void binary_expand_op(emxArray_real_T *in1, const emxArray_real_T *in2);

/* Function Definitions */
/*
 * Arguments    : emxArray_real_T *in1
 *                const emxArray_real_T *in2
 * Return Type  : void
 */
static void binary_expand_op(emxArray_real_T *in1, const emxArray_real_T *in2)
{
  emxArray_real_T *b_in1;
  const double *in2_data;
  double *b_in1_data;
  double *in1_data;
  int aux_0_1;
  int aux_1_1;
  int b_loop_ub;
  int i;
  int i1;
  int loop_ub;
  int stride_0_0;
  int stride_0_1;
  int stride_1_0;
  int stride_1_1;
  in2_data = in2->data;
  in1_data = in1->data;
  emxInit_real_T(&b_in1, 2);
  if (in2->size[0] == 1) {
    loop_ub = in1->size[0];
  } else {
    loop_ub = in2->size[0];
  }
  stride_0_0 = b_in1->size[0] * b_in1->size[1];
  b_in1->size[0] = loop_ub;
  if (in2->size[1] == 1) {
    b_loop_ub = in1->size[1];
  } else {
    b_loop_ub = in2->size[1];
  }
  b_in1->size[1] = b_loop_ub;
  emxEnsureCapacity_real_T(b_in1, stride_0_0);
  b_in1_data = b_in1->data;
  stride_0_0 = (in1->size[0] != 1);
  stride_0_1 = (in1->size[1] != 1);
  stride_1_0 = (in2->size[0] != 1);
  stride_1_1 = (in2->size[1] != 1);
  aux_0_1 = 0;
  aux_1_1 = 0;
  for (i = 0; i < b_loop_ub; i++) {
    for (i1 = 0; i1 < loop_ub; i1++) {
      double b_in1_tmp;
      double in1_tmp;
      in1_tmp = in1_data[i1 * stride_0_0 + in1->size[0] * aux_0_1];
      b_in1_tmp = in2_data[i1 * stride_1_0 + in2->size[0] * aux_1_1];
      b_in1_data[i1 + b_in1->size[0] * i] =
          in1_tmp * in1_tmp + b_in1_tmp * b_in1_tmp;
    }
    aux_1_1 += stride_1_1;
    aux_0_1 += stride_0_1;
  }
  stride_0_0 = in1->size[0] * in1->size[1];
  in1->size[0] = loop_ub;
  in1->size[1] = b_loop_ub;
  emxEnsureCapacity_real_T(in1, stride_0_0);
  in1_data = in1->data;
  for (i = 0; i < b_loop_ub; i++) {
    for (i1 = 0; i1 < loop_ub; i1++) {
      in1_data[i1 + in1->size[0] * i] = b_in1_data[i1 + b_in1->size[0] * i];
    }
  }
  emxFree_real_T(&b_in1);
}

/*
 * Arguments    : const emxArray_real_T *originalImage
 *                double threshold
 *                emxArray_uint8_T *edgeImage
 * Return Type  : void
 */
void sobel(const emxArray_real_T *originalImage, double threshold,
           emxArray_uint8_T *edgeImage)
{
  emxArray_real_T *H;
  emxArray_real_T *V;
  double *H_data;
  double *V_data;
  int i;
  int i1;
  int loop_ub;
  unsigned char *edgeImage_data;
  if (!isInitialized_sobel) {
    sobel_initialize();
  }
  /*  edgeImage = sobel(originalImage, threshold) */
  /*  Sobel edge detection. Given a normalized image (with double values) */
  /*  return an image where the edges are detected w.r.t. threshold value. */
  emxInit_real_T(&H, 2);
  conv2AXPYSameCMP(originalImage, H);
  H_data = H->data;
  emxInit_real_T(&V, 2);
  b_conv2AXPYSameCMP(originalImage, V);
  V_data = V->data;
  if ((H->size[0] == V->size[0]) && (H->size[1] == V->size[1])) {
    int scalarLB;
    int vectorUB;
    loop_ub = H->size[0] * H->size[1];
    scalarLB = (loop_ub / 2) << 1;
    vectorUB = scalarLB - 2;
    for (i = 0; i <= vectorUB; i += 2) {
      __m128d r;
      __m128d r1;
      r = _mm_loadu_pd(&H_data[i]);
      r1 = _mm_loadu_pd(&V_data[i]);
      _mm_storeu_pd(&H_data[i],
                    _mm_add_pd(_mm_mul_pd(r, r), _mm_mul_pd(r1, r1)));
    }
    for (i = scalarLB; i < loop_ub; i++) {
      H_data[i] = H_data[i] * H_data[i] + V_data[i] * V_data[i];
    }
  } else {
    binary_expand_op(H, V);
  }
  emxFree_real_T(&V);
  b_sqrt(H);
  H_data = H->data;
  loop_ub = edgeImage->size[0] * edgeImage->size[1];
  edgeImage->size[0] = H->size[0];
  edgeImage->size[1] = H->size[1];
  emxEnsureCapacity_uint8_T(edgeImage, loop_ub);
  edgeImage_data = edgeImage->data;
  loop_ub = H->size[0] * H->size[1];
  if (loop_ub < 400) {
    for (i1 = 0; i1 < loop_ub; i1++) {
      edgeImage_data[i1] =
          (unsigned char)((unsigned int)(H_data[i1] > threshold) * 255U);
    }
  } else {
#pragma omp parallel for num_threads(omp_get_max_threads())

    for (i1 = 0; i1 < loop_ub; i1++) {
      edgeImage_data[i1] =
          (unsigned char)((unsigned int)(H_data[i1] > threshold) * 255U);
    }
  }
  emxFree_real_T(&H);
}

/*
 * File trailer for sobel.c
 *
 * [EOF]
 */