Generate Code to Detect Edges on Images
This example shows how to generate a standalone C library from MATLAB® code that implements a simple Sobel filter that performs edge detection on images. The example also shows how to generate and test a MEX function in MATLAB prior to generating C code to verify that the MATLAB code is suitable for code generation.
About the sobel
Function
The sobel.m
function takes an image (represented as a double matrix) and a threshold value and returns an image with the edges detected (based on the threshold value).
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);
Generate the MEX Function
Generate a MEX function using the codegen
command.
codegen sobel
Code generation successful.
Before generating C code, you should first test the MEX function in MATLAB to ensure that it is functionally equivalent to the original MATLAB code and that no run-time errors occur. By default, codegen
generates a MEX function named sobel_mex
in the current folder. This allows you to test the MATLAB code and MEX function and compare the results.
Read in the Original Image
Use the standard imread
command.
im = imread('hello.jpg');
image(im);
Convert Image to a Grayscale Version
Convert the color image (shown above) to an equivalent grayscale image with normalized values (0.0 for black, 1.0 for white).
gray = (0.2989 * double(im(:,:,1)) + 0.5870 * double(im(:,:,2)) + 0.1140 * double(im(:,:,3)))/255;
Run the MEX Function (The Sobel Filter)
Pass the normalized image and a threshold value.
edgeIm = sobel_mex(gray, 0.7);
Display the Result
im3 = repmat(edgeIm, [1 1 3]); image(im3);
Generate Standalone C Code
codegen -config coder.config('lib') sobel
Code generation successful.
Using codegen
with the -config coder.config('lib')
option produces a standalone C library. By default, the code generated for the library is in the folder codegen/lib/sobel/
.
Inspect the Generated Function
type codegen/lib/sobel/sobel.c
/* * Prerelease License - for engineering feedback and testing purposes * only. Not for sale. * File: sobel.c * * MATLAB Coder version : 24.2 * C/C++ source code generated on : 20-Jul-2024 12:21:50 */ /* 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 <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]; } i = 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, i); 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; } i = in1->size[0] * in1->size[1]; in1->size[0] = loop_ub; in1->size[1] = b_loop_ub; emxEnsureCapacity_real_T(in1, i); 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 loop_ub_tmp; 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_tmp = H->size[0] * H->size[1]; scalarLB = (loop_ub_tmp / 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_tmp; 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; i = edgeImage->size[0] * edgeImage->size[1]; edgeImage->size[0] = H->size[0]; edgeImage->size[1] = H->size[1]; emxEnsureCapacity_uint8_T(edgeImage, i); edgeImage_data = edgeImage->data; loop_ub_tmp = H->size[0] * H->size[1]; for (i = 0; i < loop_ub_tmp; i++) { edgeImage_data[i] = (unsigned char)((unsigned int)(H_data[i] > threshold) * 255U); } emxFree_real_T(&H); } /* * File trailer for sobel.c * * [EOF] */