最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to measure the length of image from a masked image using OpenCV? - Stack Overflow

programmeradmin1浏览0评论

I am new in image processing and I am trying to improve myself by doing some projects and I have a problem about my project. I have an image dataset containing lakes with their corresponding binary mask images. I want to calculate the perimeter (boundary length) and area of the lake in each image using OpenCV.

So far, I have tried Canny Edge Detection and cv2.findContours() to detect the lake boundaries to find lake's real area and real perimeter, but I am struggling to get only the lake's contour without including unwanted edges (such as image borders or noise).

Here is my current code:

import cv2
import matplotlib.pyplot as plt
import numpy as np



# Load the image
image = cv2.imread("water_body_3.jpg")

# Apply Canny edge detection
edged = cv2.Canny(image, 50, 100)

# Find contours
contours, _ = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

largest_contour = max(contours, key=cv2.contourArea)

# Draw the detected contours
cv2.drawContours(image, [largest_contour], -1, (0, 255, 0), 2)

plt.imshow(image, cmap="gray")
plt.title("Canny Edge Detection")
plt.axis("off")
plt.show()

Problems I'm Facing:

  • The detected contours sometimes include the image border or noise instead of just the lake.

  • I want to calculate the perimeter and area of the lake, but I am not sure if I am selecting the correct contour.

Expected Output:

  • Extract only the lake contour from the image.

  • Compute the perimeter and area .

Question:

  • How can I ensure that I am only selecting the lake's contour and not unwanted edges?

  • What is the best way to calculate the lake's area and perimeter
    correctly?

  • Should I preprocess the image differently (e.g., thresholding,
    morphological operations)?

Images:

code output image: .png

mask image that I use in code (water_body_3.jpg): .jpg

original image:.jpg

I am new in image processing and I am trying to improve myself by doing some projects and I have a problem about my project. I have an image dataset containing lakes with their corresponding binary mask images. I want to calculate the perimeter (boundary length) and area of the lake in each image using OpenCV.

So far, I have tried Canny Edge Detection and cv2.findContours() to detect the lake boundaries to find lake's real area and real perimeter, but I am struggling to get only the lake's contour without including unwanted edges (such as image borders or noise).

Here is my current code:

import cv2
import matplotlib.pyplot as plt
import numpy as np



# Load the image
image = cv2.imread("water_body_3.jpg")

# Apply Canny edge detection
edged = cv2.Canny(image, 50, 100)

# Find contours
contours, _ = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

largest_contour = max(contours, key=cv2.contourArea)

# Draw the detected contours
cv2.drawContours(image, [largest_contour], -1, (0, 255, 0), 2)

plt.imshow(image, cmap="gray")
plt.title("Canny Edge Detection")
plt.axis("off")
plt.show()

Problems I'm Facing:

  • The detected contours sometimes include the image border or noise instead of just the lake.

  • I want to calculate the perimeter and area of the lake, but I am not sure if I am selecting the correct contour.

Expected Output:

  • Extract only the lake contour from the image.

  • Compute the perimeter and area .

Question:

  • How can I ensure that I am only selecting the lake's contour and not unwanted edges?

  • What is the best way to calculate the lake's area and perimeter
    correctly?

  • Should I preprocess the image differently (e.g., thresholding,
    morphological operations)?

Images:

code output image: https://i.sstatic/mLCvT5sD.png

mask image that I use in code (water_body_3.jpg): https://i.sstatic/psXSsJfg.jpg

original image:https://i.sstatic/4v6f4wLj.jpg

Share Improve this question edited Mar 19 at 11:35 NewPartizal asked Mar 19 at 11:20 NewPartizalNewPartizal 1,1383 gold badges22 silver badges56 bronze badges 2
  • 1 you can use max(contours, key = cv2.contourArea, reverse = True) to get the largest contour (your lake). If you would like to study the area of your other small contours, then check out my answer here. Consider doing some morphological operations to remove these small areas before detecting your lake, like an erode followed by a dilate (otherwise known as an opening). P.S. don't use canny, just go with the detection of the largest contour by area – Tino D Commented Mar 19 at 13:37
  • 2 From your mask, do a morphology open with a kernel large enough to only have the main area left. Then get the one contour from the main area in your processed mask. Then get the perimeter. Also just use cv2.RET_EXTERNAL. You only need external contours – fmw42 Commented Mar 19 at 22:16
Add a comment  | 

1 Answer 1

Reset to default 1

Take a look at my code : Its not only a problem of contour detection cause the contour was around your image. that's why before the detection i just draw a rectangle of 1 pixel to the image borders. And then the idea is to detect the biggest white area and making all white in this area to avoid some small noise and make a full black mask of the image then all is clean and it come easy to get what you want!

import cv2
import numpy as np

# Draw a black border of 1 pixel around the image
cv2.rectangle(image, (0, 0), (image.shape[1] - 1, image.shape[0] - 1), (0, 0, 0), thickness=1)

# Convert the image to a binary (black & white) format
# Any pixel value greater than 127 becomes 255 (white), others become 0 (black)
_, binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)

# Find the contours (external shapes) in the binary image
# RETR_EXTERNAL: Retrieves only the outermost contour
# CHAIN_APPROX_SIMPLE: Compresses horizontal, vertical, and diagonal segments to save memory
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Identify the largest contour based on its area
largest_contour = max(contours, key=cv2.contourArea)

# Calculate the area (in pixels) of the largest contour
area = cv2.contourArea(largest_contour)

# Calculate the perimeter (length of the boundary) of the largest contour
peri = cv2.arcLength(largest_contour, True)  # 'True' means it's a closed shape

# Create text labels for area and perimeter, adding "px²" for area and "px" for perimeter
text1 = f"Area: {int(area)} px²"
text2 = f"Perimeter: {int(peri)} px"

# Create a blank mask (same size as the original image) filled with zeros (black)
mask = np.zeros_like(image)

# Fill the largest contour area with white (255) on the mask
cv2.drawContours(mask, [largest_contour], 0, 255, cv2.FILLED)

# Convert the grayscale mask to a BGR (color) image so we can draw in color
image_color = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)

# Draw the largest contour filled with green (0, 255, 0) on the color image
cv2.drawContours(image_color, [largest_contour], -1, (0, 255, 0), cv2.FILLED)

# Draw the largest contour with red (0, 0, 255) on the color image
cv2.drawContours(image_color, [largest_contour], -1,(0, 0, 255), 1)

#From here its just the codes to display the info box
(x, y) = (10, 10)
(w, h) = (140, 55)

cv2.rectangle(image_color, (x, y), (x + w, y + h), (255, 255, 255), -1)  # -1 fills the rectangle

cv2.putText(image_color, text1, (x + 10, y + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 0), 1)
cv2.putText(image_color, text2, (x + 10, y + 45), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 0), 1)

cv2.imwrite("output.jpg", image_color)
cv2.imshow("Result Image", image_color)
cv2.waitKey(0)
cv2.destroyAllWindows()

Result:

发布评论

评论列表(0)

  1. 暂无评论