Saltar al contenido

Python / OpenCV – Determinación de centroides en grupos bacterianos

Solución:

La máscara es siempre el punto débil en la identificación de objetos y el paso más importante. Esto mejorará la identificación de imágenes con un gran número de bacterias. He modificado su función e_d agregando un OPEN y otro pase ERODE con el kernal, y cambié la variable it (número de iteraciones) (a 1, 2 en lugar de 1,3) para que su código haga esto. Esto no es de ninguna manera un esfuerzo terminado, pero espero que le dé una idea de lo que podría intentar mejorar aún más. Usé las imágenes que proporcionaste y, dado que ya tienen un punto rojo, esto puede estar interfiriendo con mis imágenes de resultado … pero puedes ver que es capaz de identificar más bacterias en la mayoría. Algunos de mis resultados muestran dos puntos, y la imagen con solo una bacteria, me perdí, cada uno posiblemente porque ya estaba marcado. Pruébelo con las imágenes en bruto y vea cómo funciona.

Además, dado que las bacterias son relativamente uniformes tanto en tamaño como en forma, creo que podría trabajar con la relación y / o el promedio de altura a ancho de cada bacteria para filtrar las formas extremas (pequeñas o grandes) y las formas alargadas y delgadas. también. Puede medir suficientes bacterias para ver cuál es la longitud promedio del contorno, o la altura y el ancho, o la relación altura / ancho, etc., para encontrar tolerancias razonables en lugar de la proporción con el tamaño de la imagen en sí. Otra sugerencia sería repensar cómo se enmascaran todas las imágenes, posiblemente para intentarlo en dos pasos. Uno para encontrar el límite de la forma larga que contiene las bacterias y luego para encontrar las bacterias dentro de él. Esto supone que todas sus imágenes serán similares a estas, y si es así, puede ayudar a eliminar los golpes perdidos fuera de este límite, que nunca son bacterias.

Imagen de campo brillante n. ° 1

Imagen de campo brillante n. ° 2

Imagen de campo brillante n. ° 3

Imagen de campo brillante n. ° 4

Imagen de campo brillante n. ° 5

Imagen de campo brillante n. ° 6

Imagen de campo brillante n. ° 7

Imagen de campo brillante # 8

#!usr/bin/env python
# https://stackoverflow.com/questions/63182075/python-opencv-centroid-determination-in-bacterial-clusters
import cv2
import numpy as np
import os

kernel = np.array([[0, 0, 1, 0, 0],
                   [0, 1, 1, 1, 0],
                   [1, 1, 1, 1, 1],
                   [0, 1, 1, 1, 0],
                   [0, 0, 1, 0, 0]], dtype=np.uint8)


def e_d(image, it):
    print(it)
    image = cv2.erode(image, kernel, iterations=it)
    image = cv2.dilate(image, kernel, iterations=it)
    image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel, iterations = 1)
    image = cv2.morphologyEx(image, cv2.MORPH_ERODE, kernel, iterations = 1)
    return image


#path = r"(INSERT IMAGE DIRECTORY HERE)"
path = r"E:stackimages"
img_files = [file for file in os.listdir(path)]


def segment_index(index: int):
    segment_file(img_files[index])


def segment_file(img_file: str):
    img_path = path + "\" + img_file
    print(img_path)
    head, tail = os.path.split(img_path)
    img = cv2.imread(img_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    cv2.imshow("bacteriaImg-1", img)
    cv2.waitKey(0)
    # Applying adaptive mean thresholding
    th = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 2)
    # Removing small noise
    th = e_d(th.copy(), 1)

    # Finding contours with RETR_EXTERNAL flag and removing undesired contours and
    # drawing them on a new image.
    cnt, hie = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    cntImg = th.copy()
    for contour in cnt:
        x, y, w, h = cv2.boundingRect(contour)
        # Eliminating the contour if its width is more than half of image width
        # (bacteria will not be that big).
        
        if w > img.shape[1] / 2:
            continue
  
        else:
           
            cntImg = cv2.drawContours(cntImg, [cv2.convexHull(contour)], -1, 255, -1)


    # Removing almost all the remaining noise.
    # (Some big circular noise will remain along with bacteria contours)
    cntImg = e_d(cntImg, 2)
    cv2.imshow("bacteriaImg-2", cntImg)
    cv2.waitKey(0)

    # Finding new filtered contours again
    cnt2, hie2 = cv2.findContours(cntImg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

    # Now eliminating circular type noise contours by comparing each contour's
    # extent of overlap with its enclosing circle.
    finalContours = []  # This will contain the final bacteria contours
    for contour in cnt2:
        # Finding minimum enclosing circle
        (x, y), radius = cv2.minEnclosingCircle(contour)
        center = (int(x), int(y))
        radius = int(radius)

        # creating a image with only this circle drawn on it(filled with white colour)
        circleImg = np.zeros(img.shape, dtype=np.uint8)
        circleImg = cv2.circle(circleImg, center, radius, 255, -1)

        # creating a image with only the contour drawn on it(filled with white colour)
        contourImg = np.zeros(img.shape, dtype=np.uint8)
        contourImg = cv2.drawContours(contourImg, [contour], -1, 255, -1)

        # White pixels not common in both contour and circle will remain white
        # else will become black.
        union_inter = cv2.bitwise_xor(circleImg, contourImg)

        # Finding ratio of the extent of overlap of contour to its enclosing circle.
        # Smaller the ratio, more circular the contour.
        ratio = np.sum(union_inter == 255) / np.sum(circleImg == 255)

        # Storing only non circular contours(bacteria)
        if ratio > 0.55:
            finalContours.append(contour)

    finalContours = np.asarray(finalContours)

    # Finding center of bacteria and showing it.
    bacteriaImg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

    for bacteria in finalContours:
        M = cv2.moments(bacteria)
        cx = int(M['m10'] / M['m00'])
        cy = int(M['m01'] / M['m00'])

        bacteriaImg = cv2.circle(bacteriaImg, (cx, cy), 5, (0, 0, 255), -1)

    cv2.imshow("bacteriaImg", bacteriaImg)
    cv2.waitKey(0)


# Segment Each Image
for i in range(len(img_files)):
    segment_index(i)

Aquí hay un código que puede probar y ver si funciona para usted. Utiliza un enfoque alternativo para segmentar imágenes. Puede jugar con los parámetros para ver qué combinación le da los resultados más aceptables.

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


# Adaptive threshold params
gw = 11
bs = 7
offset = 5

bact_aspect_min = 2.0
bact_aspect_max = 10.0
bact_area_min = 20 # in pixels
bact_area_max = 1000

url = "/path/to/image"
img_color = cv2.imread(url)
img = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)
rows, cols = img.shape

img_eq = img.copy()
cv2.equalizeHist(img, img_eq)

img_blur = cv2.medianBlur(img_eq, gw)
th = cv2.adaptiveThreshold(img_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, bs, offset)

_, contours, hier = cv2.findContours(th.copy(), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
    # Filter closed contours
    rect = cv2.minAreaRect(contours[i])
    area = cv2.contourArea(contours[i])
    (x, y), (width, height), angle = rect
    if min(width, height) == 0:
        continue
        
    aspect_ratio = max(width, height) / min(width, height)
    
    if hier[0][i][3] != -1 and 
    bact_aspect_min < aspect_ratio < bact_aspect_max and 
    bact_area_min < area < bact_area_max:
        M = cv2.moments(contours[i])
        cx = int(M['m10'] / M['m00'])
        cy = int(M['m01'] / M['m00'])
        img_color = cv2.circle(img_color, (cx, cy), 3, (255, 0, 0), cv2.FILLED)

plt.imshow(img_color)

Parece que sus bacterias parecen fusionadas / superpuestas en la mayoría de las imágenes y es extremadamente difícil medir su tamaño cuando están fusionadas y separarlas. La mejor manera es ejecutar este fragmento de código en Jupyter / ipywidgets con un rango de valores de parámetros y ver qué funciona mejor. ¡Buena suerte!

EDITAR 1

He actualizado el código para usar una técnica e idea ligeramente diferente. Básicamente, usando l2 contornos (agujeros) para determinar las bacterias, esto está mucho más en línea con la forma de las bacterias. Puede, nuevamente, jugar con los parámetros para ver qué funciona mejor. El conjunto de parámetros en el código me dio resultados satisfactorios. Es posible que desee filtrar la imagen un poco más para eliminar los falsos positivos.

Se pueden usar un par de otros trucos además del del código más reciente:

  1. Pruebe ADAPTIVE_THRESH_GAUSSIAN_C
  2. Pruebe la imagen ecualizada sin desenfocar
  3. Utilice contornos de nivel 1 junto con el nivel 2
  4. Utilice diferentes restricciones de tamaño para los contornos l1 y l2.

Creo que una combinación de todos estos debería proporcionarle un resultado bastante decente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *