Canny

Canny operator is an edge detector that uses a multi-stage algorithm to detect a large range of edges in an image.

Operator Effect

Input ImageParameterOutput Image
imagethreshold1 = 100
threshold2 = 400
kernelSize = 3
norm = HB_VP_NORM_L1
overlap = 0
image

Principle

The computational process of the operator is divided into several stages:

  1. Gradient calculation

The input image is convolved both vertically and horizontally using a Sobel kernel whose size is determined by the Sobel size parameter. Then it is filtered horizontally and vertically using an edge detection filter that this produces Gx and Gy. The intensity and angle of each pixel is calculated as follows:

Intensity=dx2+dy2Intensity=\sqrt{dx^2+dy^2}

Angle=arctan(dydx)Angle=arctan(\frac{dy}{dx})

  1. Non-maximum suppression

For each pixel in the image, check if the intensity is a local maximum in the gradient direction. If yes, keep it as an edge pixel, otherwise remove it as a non-edge pixel.

  1. Double thresholding

The intensity magnitude of each pixel is compared with a strong threshold (ℎ𝑖𝑔ℎ_𝑡ℎ𝑟𝑒𝑠ℎ𝑜𝑙𝑑) and a weak threshold (𝑙𝑜𝑤_𝑡ℎ𝑟𝑒𝑠ℎ𝑜𝑙𝑑), and if the intensity is greater than the strong threshold, it is labeled as a strong edge. If the intensity is less than the weak threshold, it is labeled as no edge. Intensity values between the strong and weak thresholds are labeled as weak edges.

  1. Edge Tracking

If a weak edge is connected to a strong edge, then that weak edge becomes a strong edge. Repeat this process until all weak edges connected to strong edges are found. Then the remaining weak edges are labeled as non-edges, the edge detection result for the whole image is obtained.

API Interface

int32_t hbVPCanny(hbUCPTaskHandle_t *taskHandle,
                  hbVPImage *dstImg,
                  hbVPImage const *srcImg,
                  hbVPCannyParam const *cannyParam);

For detailed interface information, please refer to hbVPCanny.

Usage

// Include the header
#include "hobot/hb_ucp.h"
#include "hobot/vp/hb_vp.h"
#include "hobot/vp/hb_vp_canny.h"

// init Image, allocate memory for image data
hbUCPSysMem src_mem;
hbUCPMallocCached(&src_mem, src_stride * src_height, 0);
hbVPImage src_img{HB_VP_IMAGE_FORMAT_Y,
                  HB_VP_IMAGE_TYPE_U8C1,
                  src_width,
                  src_height,
                  src_stride,
                  src_mem.virAddr,
                  src_mem.phyAddr,
                  nullptr,
                  0,
                  0};

hbUCPSysMem dst_mem;
hbUCPMallocCached(&dst_mem, dst_stride * dst_height, 0);
hbVPImage dst_img{HB_VP_IMAGE_FORMAT_Y,
                  HB_VP_IMAGE_TYPE_U8C1,
                  dst_width,
                  dst_height,
                  dst_stride,
                  dst_mem.virAddr,
                  dst_mem.phyAddr,
                  nullptr,
                  0,
                  0};

// init param
hbVPCannyParam canny_param;
canny_param.threshold1 = 100;
canny_param.threshold2 = 400;
canny_param.kernelSize = 3;
canny_param.norm = HB_VP_NORM_L1;
canny_param.overlap = 0;

// init task handle and schedule param
hbUCPTaskHandle_t task_handle{nullptr};
hbUCPSchedParam sched_param;
HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param);
sched_param.backend = HB_UCP_DSP_CORE_0;

// create task
hbVPCanny(&task_handle, &dst_img, &src_img, &canny_param);

// submit task
hbUCPSubmitTask(task_handle, &sched_param);

// wait for task done 
hbUCPWaitTaskDone(task_handle, 0);

// release task handle
hbUCPReleaseTask(task_handle);

// release memory
hbUCPFree(&src_mem);
hbUCPFree(&dst_mem);