주요 콘텐츠

영상에서 경계를 검출하는 코드 생성하기

이 예제에서는 영상에서 경계 검출을 수행하는 단순한 Sobel 필터를 구현하는 MATLAB® 코드에서 독립 실형형 C 라이브러리를 생성하는 방법을 보여줍니다. 또한, C 코드를 생성하기 전에 MATLAB에서 MEX 함수를 생성하고 테스트하여 MATLAB 코드가 코드 생성에 적합한지 검증하는 방법도 보여줍니다.

sobel 함수에 대한 정보

sobel.m 함수는 영상(double형 행렬로 표현됨)과 임계값을 받고 경계가 검출된(임계값 기준) 영상을 반환합니다.

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);

MEX 함수 생성하기

codegen 명령을 사용하여 MEX 함수를 생성합니다.

codegen sobel
Code generation successful.

C 코드를 생성하기 전에 먼저 MATLAB에서 MEX 함수를 테스트하여 원래 MATLAB 코드와 기능적으로 동일한지, 런타임 오류가 발생하지 않는지 확인해야 합니다. 기본적으로 codegen은 현재 폴더에 이름이 sobel_mex인 MEX 함수를 생성합니다. 이를 통해 MATLAB 코드와 MEX 함수를 테스트하고 결과를 비교할 수 있습니다.

원래 영상 읽어오기

표준 imread 명령을 사용합니다.

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

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

영상을 회색조 버전으로 변환하기

컬러 영상(위에 표시됨)을 정규화된 값(검은색은 0.0, 흰색은 1.0)을 갖는 동등한 회색조 영상으로 변환합니다.

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

MEX 함수(Sobel 필터) 실행하기

정규화된 영상과 임계값을 전달합니다.

edgeIm = sobel_mex(gray, 0.7);

결과 표시하기

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

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

독립 실행형 C 코드 생성하기

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

-config coder.config('lib') 옵션과 함께 codegen을 사용하면 독립 실형형 C 라이브러리가 생성됩니다. 기본적으로 라이브러리에 대해 생성된 코드는 폴더 codegen/lib/sobel/에 있습니다.

생성된 함수 검사하기

type codegen/lib/sobel/sobel.c
/*
 * File: sobel.c
 *
 * MATLAB Coder version            : 25.1
 * C/C++ source code generated on  : 13-Jul-2025 16:32:56
 */

/* 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]
 */