inputImage, width, height = imageReadL(pathToDir + imageName)

# Show input image
showImageL(inputImage)

# We apply Canny to obtain the edges from the image
# but also need the results of the Sobel operator (Gradient)
magnitude, angle, mX, mY = applyCannyEdgeDetector(inputImage,
                                                  GaussianKernelSize,
                                                  sobelKernelSize, upperT,
                                                  lowerT, True)

# Obtain gradient of gradient
# We apply 4 convolutions, but these can be computed in a single image pass
sobelX, sobelY = createSobelKernel(GaussianKernelSize)
mXx = applyKernelF(mX, sobelX)
mXy = applyKernelF(mX, sobelY)
mYx = applyKernelF(mY, sobelX)
mYy = applyKernelF(mY, sobelY)

# Compute curvature
curvature = createImageF(width, height)
for x, y in itertools.product(range(0, width), range(0, height)):
    # If it is an edge
    if magnitude[y, x] > 0:
        Mx2, My2, MxMy = mX[y, x] * mX[y, x], mY[y, x] * mY[y, x], mX[
            y, x] * mY[y, x]

        if Mx2 + My2 != 0.0:
            p = 1.0 / pow((Mx2 + My2), 1.5)
def applyCannyEdgeDetector(inputImage, GaussianKernelSize, sobelKernelSize, upperT, lowerT, returnGradient = False):
    
    height = len(inputImage)
    width = len(inputImage[0])    
    
    normalizeMagnitude = True
    windowDelta = 1
    
    # Apply Gaussian kernel
    gaussianKernel = createGaussianKernel(GaussianKernelSize)
    gaussianImage = applyKernelF(inputImage, gaussianKernel)
    
    # Apply Sobel kernel. We use normalized magnitude in this example
    sobelX, sobelY = createSobelKernel(sobelKernelSize)
    magnitude, angle, mX, mY = applyKernelMA(gaussianImage, sobelX, sobelY, normalizeMagnitude)
    
    # Weight magnitude by the variance. This is useful for corner extraction since suppress the internal corner
    weightedMagnitude = createImageF(width, height)
    for x,y in itertools.product(range(0, width), range(0, height)):
        sumKernel = 1.0/8.0
        for wx,wy in itertools.product(range(-1,2), range(-1, 2)):
 
            posY = y + wy
            posX = x + wx 
            
            if posY > -1 and posY <  height and  posX > -1 and posX <  width:
                sumKernel += abs(float(inputImage[posY,posX]) -  float(inputImage[y,x]))
    
        sumKernel /= 8.0
        weightedMagnitude[y,x] = magnitude[y,x] *  sumKernel

    # To store maximum suppression image
    maxImage = createImageF(width, height)
    
    # Non-maximum suppression
    border = GaussianKernelSize
    for x,y in itertools.product(range(border, width - border), 
                                 range(border, height - border)):
        
        # Only potential edges can be maximum
        if magnitude[y,x] > lowerT:     
           
            # The normal angle is perpendicular to the edge angle
            normalAngle = angle[y,x] - pi / 2.0
            
            # Make sure the angle is between 0 and pi 
            while normalAngle < 0:
                normalAngle += pi
            while normalAngle > pi:
                normalAngle -= pi
            
            # Angle defining the first point
            baseAngle = int( 4 * normalAngle / pi ) * (pi / 4.0)
            
            # Integer delta positions for interpolation
            # We use -y since the image origin is in top corner
            x1, y1 = int(round(cos(baseAngle))), -int(round(sin(baseAngle)))
            x2, y2 = int(round(cos(baseAngle + pi / 4.0))),                     \
                    -int(round(sin(baseAngle + pi / 4.0)))
            
            # How far we are from (x1,y1). Maximum difference is math.pi / 4.0, so we multiply by 2
            w = cos(2.0*(normalAngle - baseAngle))
            
            # Point to interpolate
            M1 = w * weightedMagnitude[y+y1,x+x1] + (1.0 - w) * weightedMagnitude[y+y2,x+x2]
            
            # Point to interpolate for pixels in the other side of the edge
            M2 = w * weightedMagnitude[y-y1,x-x1] + (1.0 - w) * weightedMagnitude[y-y2,x-x2]
             
            # Determine if it is a maximum. If so make sure it will be preserved 
            if weightedMagnitude[y,x] > M1 and weightedMagnitude[y,x] > M2:
                maxImage[y,x] = magnitude[y,x]

    # To compute hysteresis thresholded images we require two thresholds
    edges = createImageF(width, height)
    potentialEdges = [ ]

    # Divide pixels as edges, no edges and we are not sure
    for x,y in itertools.product(range(1, width-1), range(1, height-1)):            
        # These are edges
        if maxImage[y,x] > upperT:
            edges[y,x] = 255
            
        # These are pixels that we do not want as edges   
        if maxImage[y,x] < lowerT:
            edges[y,x] = 0
            
        # These may be edges    
        if maxImage[y,x] > lowerT and maxImage[y,x] <= upperT:
            edges[y,x] = 128

    # Resolve the potential edges
    for x,y in itertools.product(range(1, width-1), range(1, height-1)):            
        # For each edge
        if edges[y,x] == 255:
            
            # Examine neighbour
            potentialEdges = [ ]
            for wx,wy in itertools.product(range(-windowDelta, windowDelta+1), range(-windowDelta, windowDelta+1)):                  
                # It becomes an edge
                if edges[y+wy,x+wx] == 128:
                    edges[y+wy,x+wx] = 255
                    potentialEdges.append((y+wy,x+wx))
            
            # Look into new edges
            while len(potentialEdges) > 0:
                # Take element from potential edges
                y = (potentialEdges[0])[0]
                x = (potentialEdges[0])[1]
                potentialEdges = potentialEdges[1:]
                
                # Examine neighbour
                for wx,wy in itertools.product(range(-windowDelta, windowDelta+1), range(-windowDelta, windowDelta+1)): 
                    # It becomes an edge
                    if edges[y+wy,x+wx] == 128:
                        edges[y+wy,x+wx] = 255
                        potentialEdges.append((y+wy,x+wx))
        
    # Clean up remaining potential edges                            
    for x,y in itertools.product(range(1, width-1), range(1, height-1)): 
        if edges[y,x] == 128:
            edges[y,x] = 0
    
    if returnGradient == False:
        return edges, angle 
 
    return edges, angle , mX, mY
GaussianKernelSize = 4
sobelKernelSize = 3
normalizeMagnitude = True
upperT = 0.25
lowerT = 0.1
windowDelta = 2

# Read image into array
inputImage, width, height = imageReadL(pathToDir + imageName)

# Show input image
showImageL(inputImage)

# Apply Gaussian kernel
gaussianKernel = createGaussianKernel(GaussianKernelSize)
gaussianImage = applyKernelF(inputImage, gaussianKernel)

# Apply Sobel kernel. We use normalized magnitude in this example
sobelX, sobelY = createSobelKernel(sobelKernelSize)
magnitude, angle, _, _ = applyKernelMA(gaussianImage, sobelX, sobelY,
                                       normalizeMagnitude)

# To store maximum suppression image
maxImage = createImageF(width, height)

# Non-maximum suppression
border = GaussianKernelSize
for x,y in itertools.product(range(border, width-border),                   \
                             range(border, height-border)):

    # Only potential edges can be maximum
pathToDir = "../../Images/Chapter4/Input/"
imageName = "Lizard.png"
kernelSize = 12
sigma = 2

# Read image into array
inputImage, width, height = imageReadL(pathToDir + imageName)

# Show input image
showImageL(inputImage)

# Create Kernel
kernelLaplacian = createLaplacianKernel(kernelSize, sigma)

# Apply kernel
gaussianImage = applyKernelF(inputImage, kernelLaplacian)

# Zero-crossing detector
edges = createImageL(width, height)
kernelCentre = int((kernelSize - 1) / 2)
for x, y in itertools.product(range(1, width - 1), range(1, height - 1)):
    quadrantValue = [0.0, 0.0, 0.0, 0.0]
    for wx, wy in itertools.product(range(-1, 1), range(-1, 1)):
        quadrantValue[0] += gaussianImage[y + wy, x + wx]

    for wx, wy in itertools.product(range(-1, 1), range(0, 2)):
        quadrantValue[1] += gaussianImage[y + wy, x + wx]

    for wx, wy in itertools.product(range(0, 2), range(-1, 1)):
        quadrantValue[2] += gaussianImage[y + wy, x + wx]
imageName = "Logs.png"
suppWindow = 5

# Read image into array and show
inputImage, width, height = imageReadL(pathToDir + imageName)
showImageL(inputImage)

# Apply Sobel kernel. We use normalized magnitude in this example
sobelX, sobelY = createSobelKernel(3)
normalizeMagnitude = False
magnitude, _, _, _ = applyKernelMA(inputImage, sobelX, sobelY,
                                   normalizeMagnitude)
showImageF(magnitude)

# Apply Gaussian kernel
gaussianKernel = createGaussianKernel(10)
gaussianImage = applyKernelF(magnitude, gaussianKernel)

# Invert the image and add all pixels to the shape
shapeImage = []
distanceImage = createImageF(width, height)
maxGradient, minGradient = imageMaxMin(gaussianImage)
for x, y in itertools.product(range(0, width), range(0, height)):
    distanceImage[y, x] = maxGradient - gaussianImage[y, x]
    shapeImage.append((y, x))
showImageF(distanceImage)

# Watershed of the distance image
watershed = watherShed(distanceImage, shapeImage, suppWindow)
showImageF(watershed)