1

I would like to count the number of sheets in a stack, as you can see in a side view of the stack.

I have already implemented some solutions but It did not work. I still get the number of the lines 0 as output. Is there anyone who may support me to fix it?

Fyi: The output image is attached after canny edge detection. Thanks in advance!

import numpy as np
import os
import cv2
import matplotlib.pyplot as plt
import scipy
from skimage import io


def Canny_detector(img, weak_th=None, strong_th=None):
    # conversion of image to grayscale
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Noise reduction step
    img = cv2.GaussianBlur(img, (5, 5), 1.4)

    # Calculating the gradients
    gx = cv2.Sobel(np.float32(img), cv2.CV_64F, 1, 0, 3)
    gy = cv2.Sobel(np.float32(img), cv2.CV_64F, 0, 1, 3)

    # Conversion of Cartesian coordinates to polar
    mag, ang = cv2.cartToPolar(gx, gy, angleInDegrees=True)

    # setting the minimum and maximum thresholds
    # for double thresholding
    mag_max = np.max(mag)
    if not weak_th: weak_th = mag_max * 0.1
    if not strong_th: strong_th = mag_max * 0.5

    # getting the dimensions of the input image
    height, width = img.shape

    # Looping through every pixel of the grayscale
    # image
    for i_x in range(width):
        for i_y in range(height):

            grad_ang = ang[i_y, i_x]
            grad_ang = abs(grad_ang - 180) if abs(grad_ang) > 180 else abs(grad_ang)
            #print("yyy")
            # selecting the neighbours of the target pixel
            # according to the gradient direction
            # In the x axis direction
            if grad_ang <= 22.5:
                neighb_1_x, neighb_1_y = i_x - 1, i_y
                neighb_2_x, neighb_2_y = i_x + 1, i_y

            # top right (diagonal-1) direction
            elif grad_ang > 22.5 and grad_ang <= (22.5 + 45):
                neighb_1_x, neighb_1_y = i_x - 1, i_y - 1
                neighb_2_x, neighb_2_y = i_x + 1, i_y + 1

            # In y-axis direction
            elif grad_ang > (22.5 + 45) and grad_ang <= (22.5 + 90):
                neighb_1_x, neighb_1_y = i_x, i_y - 1
                neighb_2_x, neighb_2_y = i_x, i_y + 1

            # top left (diagonal-2) direction
            elif grad_ang > (22.5 + 90) and grad_ang <= (22.5 + 135):
                neighb_1_x, neighb_1_y = i_x - 1, i_y + 1
                neighb_2_x, neighb_2_y = i_x + 1, i_y - 1

            # Now it restarts the cycle
            elif grad_ang > (22.5 + 135) and grad_ang <= (22.5 + 180):
                neighb_1_x, neighb_1_y = i_x - 1, i_y
                neighb_2_x, neighb_2_y = i_x + 1, i_y

            # Non-maximum suppression step
            if width > neighb_1_x >= 0 and height > neighb_1_y >= 0:
                if mag[i_y, i_x] < mag[neighb_1_y, neighb_1_x]:
                    mag[i_y, i_x] = 0
                    continue

            if width > neighb_2_x >= 0 and height > neighb_2_y >= 0:
                if mag[i_y, i_x] < mag[neighb_2_y, neighb_2_x]:
                    mag[i_y, i_x] = 0

    weak_ids = np.zeros_like(img)
    strong_ids = np.zeros_like(img)
    ids = np.zeros_like(img)

    # double thresholding step
    for i_x in range(width):
        for i_y in range(height):
            grad_mag = mag[i_y, i_x]

            if grad_mag < weak_th:
                mag[i_y, i_x] = 0
            elif strong_th > grad_mag >= weak_th:
                ids[i_y, i_x] = 1
            else:
                ids[i_y, i_x] = 2

    # finally returning the magnitude of gradients of edges
    return mag

frame = cv2.imread('/Users/Projects/Image/IMG1.jpg')

print("Hi there")
# calling the designed function for finding edges


canny_img = Canny_detector(frame)

# Displaying the input and output image
plt.figure()

plot1 = plt.figure(1)
plt.imshow(frame)

plot2 = plt.figure(2)
plt.imshow(canny_img)

print("Hallo Hallo")
plt.show()

#J. Canny. 1986. (Canny)
#Smooth Image with Gaussian filter
#Compute Derivative of filtered image
#Find Magnitude and Orientation of gradient
#Apply Non-max suppression
#Apply Thresholding (Hysteresis)

rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 15  # minimum number of votes (intersections in Hough grid cell)
min_line_length = 50  # minimum number of pixels making up a line
max_line_gap = 20  # maximum gap in pixels between connectable line segments
line_image = np.copy(frame) * 0  # creating a blank to draw lines on



print(rho)
print("Hey there")
# After you apply Hough on edge detected image. Let's define the function which turns 
these edges into lines

canny_img = canny_img.astype(np.uint8)
lines = cv2.HoughLinesP(canny_img, rho, theta, threshold, np.array([]),
                min_line_length, max_line_gap)
print(lines)

# calculate the distances between points (x1,y1), (x2,y2) :
distance = []
for line in lines:
    distance.append(np.linalg.norm(line[:,:2] - line[:,2:]))
    print(distance)

print('max distance:', max(distance), '\nmin distance:', min(distance))

# Adjusting the best distance
bestDistance=1110

numberOfLines=[]
count=0
for x in distance:
    if x>bestDistance:
        numberOfLines.append(x)
        count=count+1

print('Number of lines:', count)
Kahraman
  • 33
  • 8
  • You should also include the input image, this will make it easier to help you! – Kartoffelkultur Oct 06 '21 at 12:35
  • Lines and edges are not the same thing. https://stackoverflow.com/a/50359884/7328782 – Cris Luengo Oct 06 '21 at 23:46
  • Canny is rarely the solution and often only worsens the problem. in this case, it is definitely not helping. -- you need to sample a horizontal line of your image. then plot it. you'll see peaks and dips. you need to find local maxima. – Christoph Rackwitz Oct 07 '21 at 00:53
  • this type of question comes up sometimes. you aren't the first who wants to count sheets in a stack of something (glass panes, sheets of leather, ...) I'm inclined to revise the whole question so it becomes actually useful. I don't know how far it is "acceptable" to go. – Christoph Rackwitz Oct 07 '21 at 00:55

1 Answers1

2

here I use canny edge detection and the probabilistic Hough transform to detect your lines.

First imports and edge detection

import cv2
import numpy as np
from matplotlib import pyplot as plt
import sympy as sp
sp.var('b,x')

img0 = cv2.imread('eRVR4.jpg',)
gray = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
for _ in range(5):
    gray = cv2.GaussianBlur(gray,(3,3),0)

gray = cv2.Canny(gray, 100, 200)
plt.imshow(gray,cmap='gray')

edge detection

Next I use the probabilistic Hough transform cv2.HoughLinesP and plot the result.

img = np.zeros(gray.shape)

lines = cv2.HoughLinesP(gray.copy(),1, np.pi/180, 100, minLineLength=150, maxLineGap=25)
mid_xs = []

for x1, y1, x2, y2 in lines.reshape(-1,4):
    slope = (y2-y1)/(x2-x1)
    intercept = sp.solve(sp.Eq(slope*x1+b,y1))[0]
    mid_x = float(sp.solve(slope*x+intercept-200)[0])
    mid_xs.append(mid_x)
    cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 2)
    
mid_xs = np.sort(np.array(mid_xs))
plt.imshow(img, cmap='gray')

Hough Line

This looks almost perfect but has two pairs of lines with almost zero distance and one pair with a way to small distance. My idea here was that I want to calculate the distances and remove outliers. Then to get a nice result plot I look if removing the line before or after the to small gap helps the most and do that.

distances = mid_xs[1:]-mid_xs[:-1]
median = np.median(distances)

@np.vectorize
def decide_what_to_remove(i):
    arr = mid_xs[i-1:i+3]
    mse1 = calc_mean_sq_error(np.delete(arr,1))
    mse2 = calc_mean_sq_error(np.delete(arr,2))
    return i if mse1 < mse2 else i+1
    
def calc_mean_sq_error(arr):
    distances = arr[1:]-arr[:-1]
    return np.mean((distances-median)**2)


problematic_distances = np.where(np.abs(distances-median) > 15)[0]
to_delete = decide_what_to_remove(problematic_distances)
mid_xs = np.delete(mid_xs, to_delete, axis=0)

Finally I can mark the lines in the original picture to check whether I have achieved what I wanted.

img = cv2.imread('eRVR4.jpg')

for x in mid_xs:
    cv2.circle(img,(int(x),200),5,(0, 255, 0),2)

plt.imshow(img, cmap='gray')

marks in original picture

Oh and len(mid_xs) tells me I have marked 23 lines.

Lukas S
  • 3,212
  • 2
  • 13
  • 25
  • Thank you for your support so far. Unfortunately, I am getting an error which is "TypeError: only integer scalar arrays can be converted to a scalar index" and it says that the 'x' is & 'b' are unresolved reference. – Kahraman Oct 07 '21 at 14:17
  • @Kahraman ups then I must have made a copy paste mistake when I moved this. Can you be a bit more specific in which part of the code so I can fix it? – Lukas S Oct 07 '21 at 14:22
  • I see. Full error would be: decide_what_to_remove -> arr = mid_xs[i - 1:i + 3] TypeError: only integer scalar arrays can be converted to a scalar index. – Kahraman Oct 07 '21 at 14:27
  • I think that the second problem is initialization of 'x' and 'b' which can be found in the following for loop -. for x1, y1, x2, y2 in lines.reshape(-1, 4): – Kahraman Oct 07 '21 at 14:29
  • Nope it seems that the problem is on your end. I literally copy pasted my code from here back and it ran without a problem. Can you try to reproduce that? I suggest just for testing to get a fresh folder download your input image in there copy past my code and try it. If that does not work maybe post what versions of python, cv2, numpy, matplotlib and sympy you have installed. – Lukas S Oct 07 '21 at 14:37
  • @Kahraman my guess is that you need slightly different parameters for your big image compared to the one you provided online for testing. – Lukas S Oct 07 '21 at 14:46
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/237925/discussion-between-kahraman-and-user2640045). – Kahraman Oct 07 '21 at 15:02
  • @Kahraman I am sorry unfortunately I have an appointment coming up and need to leave my computer. I‘ll write you later or tomorrow depending on how long it will take. – Lukas S Oct 07 '21 at 15:16
  • Okay. Thank you! – Kahraman Oct 07 '21 at 15:23
  • @Kahraman Hi, did you get it to work or can I help you with it now? – Lukas S Oct 08 '21 at 16:08
  • Hi, I still get an error. I would appreciate your help. – Kahraman Oct 08 '21 at 17:35
  • @Kahraman I wrote you in your chatroom. – Lukas S Oct 09 '21 at 10:27
  • I texted you the error in the chatroom and would be happy if you may have a look at it when you have time. Thanks – Kahraman Oct 13 '21 at 13:12
  • @Kahraman I answered there – Lukas S Oct 13 '21 at 13:45
  • I texted you via chat room. Thanks – Kahraman Oct 18 '21 at 13:32
  • @Kahraman I wrote you in your chatroom. – Lukas S Oct 23 '21 at 19:43
  • @Kahraman Hahaha great that it finally worked out. – Lukas S Oct 25 '21 at 22:43