Canny's Algorithm: Hysteresis Mal-function

839 views Asked by At

I am writing Canny's Algorithm, and I seem to have an issue with hysteresis. The Thresholds Appears to process, however my hysteresis does not seem to function at all. As well as method remove weak for some odd reason. Please help!

Low @ 10 Low @ 10 High @ 75 High @ 75 After Hysteresis After Hysteresis, with problem A, edges were not strengthen with method performHysteresis; B weak non-edges are not removed with method removeWeak.

Source code for the method as follows:

import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;

class CannyMethod {

private static final float[] sobelX = { 1.0f, 0.0f, -1.0f,
                                        2.0f, 0.0f, -2.0f,
                                        1.0f, 0.0f, -1.0f};
private static final float[] sobelY = { 1.0f, 2.0f, 1.0f,
                                        0.0f, 0.0f, 0.0f,
                                       -1.0f,-2.0f,-1.0f};
private static int low, high;


public CannyMethod() {}

private ConvolveOp getSobel(boolean xy) {
    Kernel kernel;
    if (xy) kernel = new Kernel(3, 3, sobelX);
    else kernel = new Kernel(3, 3, sobelY);

    return new ConvolveOp(kernel, ConvolveOp.EDGE_ZERO_FILL, null);
}

public BufferedImage getCannyFilter(BufferedImage img) {
    return getCannyFilter(img, low, high);
}

public BufferedImage getCannyFilter(BufferedImage img, int l, int h) {
    int width = img.getWidth();
    int height = img.getHeight();
    low = l;
    high = h;

    int size = width * height;
    int[] x = new int[size];
    int[] y = new int[size];
    int[] pixelM = new int[size];
    double[] pixelD = new double[size];
    int[] pixelNew = new int[size];

    BufferedImage sobelXImg = getSobel(true).filter(img, null);
    BufferedImage sobelYImg = getSobel(false).filter(img, null);


    // returns arrays for x and y direction after convultion with Sobel Operator
    sobelXImg.getRaster().getPixels(0, 0, width, height, x);
    sobelYImg.getRaster().getPixels(0, 0, width, height, y);

// Calculates Gradient and Magnitude
    for(int i = 0; i < size; i++) {
        pixelM[i] = (int) Math.hypot(x[i], y[i]);
        pixelD[i] = Math.atan2((double) y[i], (double) x[i]);
    }

//Operations for Canny Algorithm takes magnitude and gradient and input into new array fo WritableRaster
    normalizeDirection(pixelD);
    nonMaximaSupression(pixelM, pixelD, pixelNew, width, height);
    performHysteresis(pixelNew, width);
    removeWeak(pixelNew);

    BufferedImage result = 
        new BufferedImage(width, height,
                          BufferedImage.TYPE_BYTE_GRAY);
    result.getRaster().setPixels(0, 0, width, height, pixelNew);

    return result;
}

private void normalizeDirection(double[] dArray) {
//Round degrees

    double pi = Math.PI;
    for(double i : dArray) {
        if (i < pi/8d && i >= -pi/8d) i = 0;
        else if (i < 3d*pi/8d && i >= pi/8d) i = 45;
        else if (i < -3d*pi/8d || i >= 3d*pi/8d) i = 90;
        else if (i < -pi/8d && i >= -3d*pi/8d) i = 135;
    }
}

private void nonMaximaSupression(int[] pixelM, double[] pixelD, 
    int[] pixelNew, int width, int height) {
//non-Maxima Supression
//Since array is not in 2-D, positions are calulated with width - functions properly

    for(int i = 0; i < pixelNew.length; i++) {
        if (i % width == 0 || (i + 1) % width == 0 ||
            i <= width || i >= width * height - 1) pixelNew[i] = 0;
        else {
            switch ((int) pixelD[i]) {
                case 0:  if (pixelM[i] > pixelM[i+1] 
                            && pixelM[i] > pixelM[i-1])
                            setPixel(i, pixelM[i], pixelNew);
                         else pixelNew[i] = 0;
                         break;
                case 45: if (pixelM[i] > pixelM[i+(width-1)] 
                            && pixelM[i] > pixelM[i-(width-1)])
                            setPixel(i, pixelM[i], pixelNew);
                         else pixelNew[i] = 0;
                         break;
                case 90: if (pixelM[i] > pixelM[i+width] 
                            && pixelM[i] > pixelM[i-width])
                            setPixel(i, pixelM[i], pixelNew);
                         else pixelNew[i] = 0;
                         break;
                case 135:if (pixelM[i] > pixelM[i+width] 
                            && pixelM[i] > pixelM[i-width])
                            setPixel(i, pixelM[i], pixelNew);
                         else pixelNew[i] = 0;
                         break;
                default: pixelNew[i] = 0;
            }
        }
    }
}

private void performHysteresis(int[] array, int width) {
//performs hysteresis

    int[] temp;
    for(int i = width; i < array.length - width; i++) {
        if (i % width == 0 || (i + 1) % width == 0) {}
        else {
            if (array[i] == 255) {
    //found strong one, track surrounding weak ones
    //temp is the positions of surrounding pixels
                temp = new int[] 
                    {i - (width + 1), i - width, i - (width - 1),
                     i - 1,                      i + 1,
                     i + (width - 1), i + width, i + (width + 1)};
                trackWeak(array, temp, width);
            }
        }
    }
}

private void trackWeak(int[] array, int[] pos, int width) {
    int[] temp;
    for (int i : pos) {
        if (array[i] > 0 && array[i] < 255) {
            array[i] = 255;
    //set weak one to strong one

            if (i % width == 0 || (i + 1) % width == 0) {}
            else {
    //temp is the positions of surrounding pixels
                temp = new int[]
                    {i - (width + 1), i - width, i - (width - 1),
                     i - 1,                      i + 1,
                     i + (width - 1), i + width, i + (width + 1)};
                trackWeak(array, temp, width);
            }
        }
    }
}

private void removeWeak(int[] array) {
//remove remaining weak ones from lew Threshold

    for(int i : array) {
        if (i < 255) {i = 0;}
    }
}

private void setPixel(int pos, int value, int[] pixelNew) {
    if (value > high) pixelNew[pos] = 255;
    else if (value > low) pixelNew[pos] = 128;
    else pixelNew[pos] = 0;
}

public void setThreshold(int l, int h) {
    low = l;
    high = h;
}
}
1

There are 1 answers

0
DVCode On BEST ANSWER

I figured it out. The hysteresis was working, it was just hard to tell given the quality of the image.

As for the remove weak, I used the enhanced for loop which I am starting to see that only a copy of the element is obtained and changed, not actually the element in the array itself. Once I changed that to a regular for loop it worked!