Skip to content

Python/OpenCV - Determinazione del centroide nei cluster batterici

Non hai più bisogno di indagare su altre pagine perché sei arrivato nello spazio giusto, abbiamo la risposta che cerchi senza problemi.

Soluzione:

La maschera è sempre il punto debole nell'identificazione degli oggetti e il passo più importante. Questo migliorerà l'identificazione delle immagini con un numero elevato di batteri. Ho modificato la vostra funzione e_d aggiungendo un passaggio OPEN e un altro ERODE con il kernal, e ho cambiato la variabile it (numero di iterazioni) (a 1, 2 invece di 1,3) per il vostro codice. Questo non è assolutamente un lavoro finito, ma spero che vi dia un'idea di cosa potreste provare a migliorare ulteriormente. Ho usato le immagini che mi avete fornito e, poiché hanno già un punto rosso, questo potrebbe interferire con i miei risultati... ma potete vedere che è in grado di identificare più batteri nella maggior parte dei casi. Alcuni dei miei risultati mostrano due punti e l'immagine con un solo batterio mi è sfuggita, probabilmente perché era già contrassegnata. Provatelo con le immagini grezze e vedete come si comporta.

Inoltre, poiché i batteri sono relativamente uniformi sia in termini di dimensioni che di forma, penso che si possa lavorare con il rapporto e/o la media tra altezza e larghezza di ciascun batterio per filtrare le forme estreme (piccole o grandi) e le forme magre e lunghe. Si possono misurare abbastanza batteri per vedere qual è la lunghezza media del contorno, o l'altezza e la larghezza, o il rapporto altezza/larghezza, ecc. per trovare tolleranze ragionevoli piuttosto che la proporzione con le dimensioni dell'immagine stessa. Un altro suggerimento sarebbe quello di ripensare al modo in cui state mascherando le immagini, magari provando a farlo in due fasi. Una per trovare il confine della forma lunga che contiene i batteri, e poi per trovare i batteri all'interno di essa. Questo presuppone che tutte le immagini siano simili a queste e, se così fosse, potrebbe aiutare a eliminare i risultati vaganti al di fuori di questo confine, che non sono mai batteri.

Immagine in campo luminoso #1

Immagine in campo luminoso n. 2

Immagine in campo luminoso #3

Immagine in campo luminoso #4

Immagine in campo luminoso #5

Immagine in campo luminoso #6

Immagine in campo luminoso #7

Immagine in campo luminoso #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)

Ecco un codice che potete provare per vedere se funziona per voi. Utilizza un approccio alternativo alla segmentazione delle immagini. Si può giocare con i parametri per vedere quale combinazione dà i risultati più accettabili.

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)

Sembra che i batteri siano fusi/sovrapposti nella maggior parte delle immagini ed è estremamente difficile valutare le loro dimensioni quando sono fusi e separarli. Il modo migliore è eseguire questo frammento di codice in Jupyter/ipywidgets con una serie di valori di parametri e vedere cosa funziona meglio. Buona fortuna!

MODIFICA 1

Ho aggiornato il codice per utilizzare una tecnica e un'idea leggermente diversa. Fondamentalmente uso i contorni l2 (fori) per accertare i batteri, questo è molto più in linea con la forma dei batteri. È possibile, ancora una volta, giocherellare con i parametri per vedere cosa funziona meglio. L'insieme dei parametri del codice mi ha dato risultati soddisfacenti. È possibile filtrare ulteriormente l'immagine per eliminare i falsi positivi.

È possibile utilizzare un paio di altri trucchi oltre a quello descritto nell'ultimo codice:

  1. Provare ADAPTIVE_THRESH_GAUSSIAN_C
  2. Prova l'immagine equalizzata senza sfocatura
  3. Utilizzare i contorni di livello 1 insieme a quelli di livello 2
  4. Utilizzare vincoli di dimensioni diverse per i contorni l1 e l2.

Penso che una combinazione di tutte queste opzioni dovrebbe fornire un risultato abbastanza decente.

Se scorri trovi le note di altri amministratori, hai comunque la possibilità di mostrare le tue se lo desideri.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.