admin管理员组

文章数量:1401969

I have an formatted images like this : Test cases

And I would like to be able to detect how many "rocks" are there and in terms of pixel , how many pixels they represents. I have try using open cv but it keeps outlining other thing other than the rocks. Are there any implementation more suitable for this?

What i have tried:

import cv2
import numpy as np
import matplotlib.pyplot as plt
image_path = "medium.png"
original_image = cv2.imread(image_path)
gray_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
binary_image = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                    cv2.THRESH_BINARY_INV, 11, 2)
_, binary_image2 = cv2.threshold(gray_image, 230, 255, cv2.THRESH_BINARY_INV)
combined_binary = cv2.bitwise_or(binary_image, binary_image2)
kernel = np.ones((2, 2), np.uint8)
cleaned_binary = cv2.morphologyEx(combined_binary, cv2.MORPH_OPEN, kernel, iterations=1)
contours, _ = cv2.findContours(cleaned_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
output_image = np.ones_like(original_image) * 255

np.random.seed(42)  
hues = np.linspace(0, 179, len(contours), dtype=np.uint8)
np.random.shuffle(hues)
colors = []
for hue in hues:
    hsv = np.array([[[hue, 255, 255]]], dtype=np.uint8)
    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)[0][0]
    colors.append(rgb.tolist())
min_area_threshold = 10 
valid_particles = 0
for i, contour in enumerate(contours):
    area = cv2.contourArea(contour)
    if area >= min_area_threshold:
        valid_particles += 1
        cv2.drawContours(output_image, [contour], -1, colors[i % len(colors)], -1) 
        cv2.drawContours(output_image, [contour], -1, (0, 0, 0), 1)
cv2.imwrite('binary_image.png', binary_image)
cv2.imwrite('binary_image2.png', binary_image2)
cv2.imwrite('combined_binary.png', combined_binary)
cv2.imwrite('cleaned_binary.png', cleaned_binary)
plt.figure(figsize=(12, 10))
plt.imshow(cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB))
plt.title(f'Total Particles Detected: {valid_particles}')
plt.axis('off')
plt.tight_layout()
plt.show()
print(f"Total number of actual particles detected: {valid_particles}")
cv2.imwrite('colored_particles_improved.png', output_image)

But still I am left with chunks as shown in this images My preprocess

Are there way to separate the chunks better?

I have an formatted images like this : Test cases

And I would like to be able to detect how many "rocks" are there and in terms of pixel , how many pixels they represents. I have try using open cv but it keeps outlining other thing other than the rocks. Are there any implementation more suitable for this?

What i have tried:

import cv2
import numpy as np
import matplotlib.pyplot as plt
image_path = "medium.png"
original_image = cv2.imread(image_path)
gray_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
binary_image = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                    cv2.THRESH_BINARY_INV, 11, 2)
_, binary_image2 = cv2.threshold(gray_image, 230, 255, cv2.THRESH_BINARY_INV)
combined_binary = cv2.bitwise_or(binary_image, binary_image2)
kernel = np.ones((2, 2), np.uint8)
cleaned_binary = cv2.morphologyEx(combined_binary, cv2.MORPH_OPEN, kernel, iterations=1)
contours, _ = cv2.findContours(cleaned_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
output_image = np.ones_like(original_image) * 255

np.random.seed(42)  
hues = np.linspace(0, 179, len(contours), dtype=np.uint8)
np.random.shuffle(hues)
colors = []
for hue in hues:
    hsv = np.array([[[hue, 255, 255]]], dtype=np.uint8)
    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)[0][0]
    colors.append(rgb.tolist())
min_area_threshold = 10 
valid_particles = 0
for i, contour in enumerate(contours):
    area = cv2.contourArea(contour)
    if area >= min_area_threshold:
        valid_particles += 1
        cv2.drawContours(output_image, [contour], -1, colors[i % len(colors)], -1) 
        cv2.drawContours(output_image, [contour], -1, (0, 0, 0), 1)
cv2.imwrite('binary_image.png', binary_image)
cv2.imwrite('binary_image2.png', binary_image2)
cv2.imwrite('combined_binary.png', combined_binary)
cv2.imwrite('cleaned_binary.png', cleaned_binary)
plt.figure(figsize=(12, 10))
plt.imshow(cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB))
plt.title(f'Total Particles Detected: {valid_particles}')
plt.axis('off')
plt.tight_layout()
plt.show()
print(f"Total number of actual particles detected: {valid_particles}")
cv2.imwrite('colored_particles_improved.png', output_image)

But still I am left with chunks as shown in this images My preprocess

Are there way to separate the chunks better?

Share Improve this question edited Mar 24 at 18:05 Razark asked Mar 24 at 16:22 RazarkRazark 93 bronze badges 4
  • 1 Questions must provide attempted code as described in How to create a Minimal, Reproducible Example. Also, this answer gives an idea of existing opencv parameters to tune blob finding: stackoverflow/a/79496778/2834978 – LMC Commented Mar 24 at 16:26
  • @LMC MREs are only required for debugging questions. They are not necessary for how-to questions. – Anerdw Commented Mar 24 at 16:27
  • 1 @Anerdw opencv is probably the top #1 tool to do that so this looks a lot like a debugging question to me. Is not clear if OP is expecting for someone to write code for him or just asking for recommendations. – LMC Commented Mar 24 at 17:08
  • My bad i was on my phone and plan to put my code later on after i get back – Razark Commented Mar 24 at 17:56
Add a comment  | 

1 Answer 1

Reset to default 0

Starting with a simpler approach, setting a high value for initial threshold and filtering contours with a parent with: hierarchy[((hierarchy[:,:,3] >= 0))]

import cv2 as cv
import numpy as np
import random as rng
rng.seed(12345)
def thresh_callback(val):
    threshold = val
    # Detect edges using Canny
    canny_output = cv.Canny(src_gray, threshold, threshold * 2)
    # Find contours
    contours, hierarchy = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
    print(f"hierarchy orig shape: {hierarchy.shape}")
    # Countours with a parent
    hierarchy = hierarchy[((hierarchy[:,:,3] >= 0))]
    print(f"hierarchy new shape: {hierarchy.shape}")

    contours = [contours[i] for i in hierarchy[:,3].tolist()]
    print(f"contours: {len(contours)}")

    # Draw contours
    drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
    for i in range(len(hierarchy)):
        color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
        cv.drawContours(drawing, contours, i, color, 2, cv.LINE_8, hierarchy, 0)
    # Show in a window
    cv.imshow('Contours', drawing)

src = cv.imread('/home/lmc/tmp/countours.png')

# Convert image to gray and blur it
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
src_gray = cv.blur(src_gray, (3,3))
# Create Window
source_window = 'Source'
cv.namedWindow(source_window)
cv.imshow(source_window, src)

thresh = 380 # initial threshold
thresh_callback(thresh)
cv.waitKey()

Further filtering can be done using contour features

本文标签: computer visionUnstructured shape recognition in pythonStack Overflow