Is anyone here familiar with implementing canny from scratch in C++? I'm having trouble with the output image of the filter. I'm using Qt to create app. I applied Sobel Sobel, Canny. I think it's an issue related to format of final edges or something, but I can't figure it out.
Sobel is working Fine, but Canny is showing the filtered image multiple times stacked together.
Canny From OpenCV Canny from scratch
void QtWidgetsApplication2::applyCannyFilter()
{
if (!img_detection.empty()) {
// Define Canny thresholds (adjust as needed)
int lowThreshold = 5;
int highThreshold = 20;
// Convert image to grayscale if it's not already
cv::Mat grayImage;
cv::cvtColor(img_detection, grayImage, cv::COLOR_BGR2GRAY);
// Apply Canny edge detection using custom function
cv::Mat edges;
cv::Canny(grayImage, edges, lowThreshold, highThreshold);
// Convert the output to QImage for display
QImage qImage(edges.data, edges.cols, edges.rows, edges.step, QImage::Format_Grayscale8);
if (qImage.isNull()) {
std::cerr << "Error: Unable to convert image to QImage." << std::endl;
return;
}
// Convert grayscale to RGB for display
cv::Mat displayImage;
cv::cvtColor(edges, displayImage, cv::COLOR_GRAY2RGB);
// Display the image in the specified QLabel
QPixmap pixmap = QPixmap::fromImage(QImage(displayImage.data, displayImage.cols, displayImage.rows, displayImage.step, QImage::Format_RGB888));
ui->outputLabel_3->setPixmap(pixmap.scaled(ui->outputLabel_3->size(), Qt::KeepAspectRatio));
ui->outputLabel_3->setAlignment(Qt::AlignCenter);
}
else {
std::cerr << "Error: No image loaded." << std::endl;
}
}
void applyCanny(const cv::Mat& input, cv::Mat& output, int lowThreshold, int highThreshold) {
// Apply Sobel to get gradient images
cv::Mat gradX, gradY;
cv::Mat out;
std::tie(gradX, gradY) = applySobel(input,out);
// Calculate gradient magnitude and direction
cv::Mat gradMagnitude = cv::Mat::zeros(input.size(), CV_32F);
cv::Mat gradDirection = cv::Mat::zeros(input.size(), CV_32F);
for (int i = 0; i < input.rows; ++i) {
for (int j = 0; j < input.cols; ++j) {
float gx = gradX.at<float>(i, j);
float gy = gradY.at<float>(i, j);
gradMagnitude.at<float>(i, j) = std::sqrt(gx * gx + gy * gy);
gradDirection.at<float>(i, j) = std::atan2(gy, gx) * 180 / CV_PI;
if (gradDirection.at<float>(i, j) < 0) {
gradDirection.at<float>(i, j) += 180; // Convert negative angles to positive
}
}
}
// Non-maximum Suppression
cv::Mat nonMaxSuppressed = cv::Mat::zeros(gradMagnitude.size(), CV_8UC1);
for (int i = 1; i < gradMagnitude.rows - 1; ++i) {
for (int j = 1; j < gradMagnitude.cols - 1; ++j) {
float angle = gradDirection.at<float>(i, j);
float mag1, mag2;
// Horizontal edge
if ((angle >= -22.5 && angle < 22.5) || (angle >= 157.5 && angle <= 180) || (angle >= 0 && angle < 22.5)) {
mag1 = gradMagnitude.at<float>(i, j - 1);
mag2 = gradMagnitude.at<float>(i, j + 1);
}
// Diagonal edge (top-left to bottom-right)
else if ((angle >= 22.5 && angle < 67.5) || (angle >= 180 && angle < 202.5)) {
mag1 = gradMagnitude.at<float>(i - 1, j - 1);
mag2 = gradMagnitude.at<float>(i + 1, j + 1);
}
// Vertical edge
else if ((angle >= 67.5 && angle < 112.5) || (angle >= 202.5 && angle < 247.5)) {
mag1 = gradMagnitude.at<float>(i - 1, j);
mag2 = gradMagnitude.at<float>(i + 1, j);
}
// Diagonal edge (bottom-left to top-right)
else {
mag1 = gradMagnitude.at<float>(i + 1, j - 1);
mag2 = gradMagnitude.at<float>(i - 1, j + 1);
}
// Perform non-maximum suppression
if (gradMagnitude.at<float>(i, j) >= mag1 && gradMagnitude.at<float>(i, j) >= mag2) {
nonMaxSuppressed.at<uchar>(i, j) = 255;
}
}
}
// Double Thresholding
cv::Mat edges = cv::Mat::zeros(nonMaxSuppressed.size(), CV_8UC1);
for (int i = 0; i < nonMaxSuppressed.rows; ++i) {
for (int j = 0; j < nonMaxSuppressed.cols; ++j) {
if (nonMaxSuppressed.at<uchar>(i, j) >= highThreshold) {
edges.at<uchar>(i, j) = 255;
}
else if (nonMaxSuppressed.at<uchar>(i, j) >= lowThreshold && nonMaxSuppressed.at<uchar>(i, j) < highThreshold) {
// Check 8-connected neighbors
if (nonMaxSuppressed.at<uchar>(i-1, j-1) >= highThreshold ||
nonMaxSuppressed.at<uchar>(i-1, j) >= highThreshold ||
nonMaxSuppressed.at<uchar>(i-1, j+1) >= highThreshold ||
nonMaxSuppressed.at<uchar>(i, j-1) >= highThreshold ||
nonMaxSuppressed.at<uchar>(i, j+1) >= highThreshold ||
nonMaxSuppressed.at<uchar>(i+1, j-1) >= highThreshold ||
nonMaxSuppressed.at<uchar>(i+1, j) >= highThreshold ||
nonMaxSuppressed.at<uchar>(i+1, j+1) >= highThreshold) {
edges.at<uchar>(i, j) = 255;
}
}
}
}
// Convert edges to CV_8U format
edges.convertTo(edges, CV_8U);
output = edges;
}