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 = 64
borderType = HB_VP_BORDER_REPLICATE
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 = 1; canny_param.overlap = 64; canny_param.borderType = 1; // 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);