1

I was reading this post to calculate the center of an image using OpenCV which uses Moments. But I am trying to calculate the center of an object I detected using HoughLinesP. Is there a way with OpenCV I could do this?

Here is the image for which I am trying to calculate the centers.

enter image description here

The line segments were found and the output image looks like:

enter image description here

import cv2
import numpy as np
import math

img = cv2.imread("./images/octa.jpg")

b,g,r = cv2.split(img)

smoothed = cv2.GaussianBlur(g, (3,3), 0)

edges = cv2.Canny(smoothed, 15, 60, apertureSize = 3)

lines = cv2.HoughLinesP(edges,1,np.pi/180,35, 30, 20)


print("length of lines detected ", lines.shape)


for line in lines:
        for x1,y1,x2,y2 in line:
          cv2.line(img,(x1,y1),(x2,y2),(255,0,0),2)
          print("x1,y1", x1,",",y1, " --- ", "x2,y2", x2,",",y2)



cv2.imshow('detected',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Using the coordinates how could I calculate the center of this image? How could I use Moments here?

One constraint I have is that I cannot use Contour methods included with OpenCV.

Sophia
  • 367
  • 1
  • 8
  • 14
  • For thia particular image it would be easier to crop it and just compute the center of mass, using scipy for example. Do you really need to calculate this from the lines? – user8408080 Jan 27 '19 at 14:02
  • @user8408080 Yes, for some reason cannot use contour methods of opencv. Could you show how could I do this? – Sophia Jan 27 '19 at 14:07
  • @user8408080 Any idea? – Sophia Jan 27 '19 at 15:49
  • I'm currently on mobile so I can't test anything right now, but I will try as soon as I get on my PC – user8408080 Jan 27 '19 at 16:15
  • Is this the only image you need to calculate the center for? Or are they all of the same type? – user8408080 Jan 27 '19 at 16:20
  • @user8408080 This is one of a type. I have images that have octagon, hexagon and need to compute the centroid for it. But each image will have either of the mentioned polygon. – Sophia Jan 27 '19 at 16:24
  • What version of openCV do you use? (type `cv2.__version__` in your shell) – user8408080 Jan 27 '19 at 16:31
  • First of all, you might want to eliminate that false positive in the bottom right corner. Dilate the edges by a 3x3 kernel, so you get better line matches. Then find convex hull of the endpoints of all the lines. That's basically a contour, so then find center of mass using moments. – Dan Mašek Jan 27 '19 at 16:42
  • @DanMašek For some reason I do not want to use contour methods by OpenCV. I want to do without it. Could you suggest something? – Sophia Jan 27 '19 at 17:40

1 Answers1

0

The following code was used with cv2 version of 3.3.1.

I closely followed the opencv docs and it worked fine.

import cv2

img = cv2.imread("octa.jpg", 0)
ret,thresh = cv2.threshold(img,100,255,0)
im2, contours, hierachy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]

M = cv2.moments(cnt)

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

im2 = cv2.cvtColor(im2, cv2.COLOR_GRAY2RGB)

cv2.polylines(im2, cnt, True, (0, 0, 255), 2)

cv2.circle(im2, (cx, cy), 5, (0, 0, 255), 1)

cv2.imshow("res", im2)

Two notes:

  • you need to add the argument 0 to imread otherwise the contour finding would not work
  • I set the threshold just a little bit lower, so only the contours of the octagon were found

Result:

result

If you use a different version of cv2, you can just change the docs to your version; the documentation is really good.

You also may want to blur your image a bit or do some other preprocessing, but in this case, there was no need for it.

EDIT Without contour:

I took the helpful comments from this post and tinkered around a bit. This does not use contours. It finds lines and uses them to find the center

import cv2
import numpy as np

mg = cv2.imread('octa.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
ret,thresh = cv2.threshold(blur_gray,100,255,0)

low_threshold = 50
high_threshold = 150
edges = cv2.Canny(thresh, low_threshold, high_threshold)

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 = 50  # maximum gap in pixels between connectable line segments
line_image = np.copy(img) * 0  # creating a blank to draw lines on

# Run Hough on edge detected image
# Output "lines" is an array containing endpoints of detected line segments
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)

for line in lines:
    for x1,y1,x2,y2 in line:
        cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),2)

lines_edges = cv2.addWeighted(img, 0.5, line_image, 1, 0)

line_image_gray = cv2.cvtColor(line_image, cv2.COLOR_RGB2GRAY)

M = cv2.moments(line_image_gray)

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

cv2.circle(lines_edges, (cx, cy), 5, (0, 0, 255), 1)

cv2.imshow("res", lines_edges)

Result: enter image description here Found lines are drawn in blue; the center in red

user8408080
  • 2,428
  • 1
  • 10
  • 19
  • The one constraint that I have, is that I cannot use `contour` methods of `OpenCV` :( – Sophia Jan 27 '19 at 17:37
  • Oh I thought, it just did not work, because you forgot to set the parameter. I will try to find a way around it. So the only function, that is not allowed is `cv2.findContours`? Or is there something else? – user8408080 Jan 27 '19 at 17:38
  • May be after recognizing octagon using HoughLinesP, I could get that part and then use `Moments` on it. But I am stuck here. – Sophia Jan 27 '19 at 17:42
  • Any method that internally uses findContours like `cv2.SimpleBlobDetectors` `cv.cornerHarris`, `cv2.connectedComponents`. – Sophia Jan 27 '19 at 17:44
  • @Sophia I hope this fits your needs now – user8408080 Jan 27 '19 at 18:58
  • Could you briefly explain what you just did? Looks like a magic to me. – Sophia Jan 27 '19 at 19:35
  • I understand the part until `lines` calculation but not really after that. What was the purpose of mixing 2 images and what does `m10` and `m01` represent? – Sophia Jan 27 '19 at 19:39
  • 1
    @Sophia It draws all the detected lines into a blank image, and then uses the variant of `cv2.moments` that takes an image instead of a list of points as an input. – Dan Mašek Jan 27 '19 at 19:59
  • @Sophia I was just mixing the two images to visualize the edges on the original image. There is no math happening there. The math is happening on an array which only contains the found lines and is completely empty otherwise (`line_image_gray`). I calculate the center using `cv2.moments` which is explained in the link in the answer. In fact I think, you can just use the `cv2.moments` function directly on the `thresh` image, but now you also have the lines, which may be helpful for you. – user8408080 Jan 27 '19 at 21:05
  • Read the links and doc. Could make sense. I was wondering if this would work if there is another shape like rectangle along with octagon and we need to find the center of octagon? What would the difference in approach be? – Sophia Jan 28 '19 at 08:28
  • Also, I tried with multiple figures in one image. It computes centroid for one image. So when we are computing centroid using `cx = int(M['m10']/M['m00'])` and `cy = int(M['m01']/M['m00'])`. What contour is being considered assuming when there are multiple contours detected using the above method? – Sophia Jan 28 '19 at 08:37
  • It would be very easy to find the center of mulitple contours, if you could use the contour methods, which really would be the best way to go. Using this kind of "custom contour finding" doesn't yield an actual contour, but just a set of lines, that the algorithm found in a preprocessed image. If you have multiple shapes in one picture (which you didn't state in the question), you may either want to utilize some automated cropping or a clustering algorithm. – user8408080 Jan 28 '19 at 11:27
  • So all in all: You should really try and get the contour method to work, because having multiple objects in one picture is a whole other problem. At this point you basically would have to write your own contour function. – user8408080 Jan 28 '19 at 11:31