简体   繁体   中英

OpenCV: Remove background noise and increase signal strength

I'm new to OpenCV and tried multiple things but still have some problems. I have images like this:

在此处输入图片说明

In the center there is a cluster (hard to see). I want to find those clusters and count them. I use cv2.findContours for this and this already works very good for images where this clusters have a good brightness and the background noise is not too strong.

With images like this, where the cluster is very dark or images where the background noise is very strong and looks very similar to the actual cluster, i have problems detecting them.

So what I would like to do now is remove the background noise, so that only the clusters are left. Then I could increase the brightness of the image and (I think) it should be easier to identify those clusters.

The background noise can very a lot! The image above is a sample, where there is not that much background noise, but it can be a lot worse. I have images where I know, that there are no clusters in it (negative control). Everything in there is just the background-noise. So my idea was to find the dominant color in this negative control:

from skimage import io
def getDominantColor(image):
        a = io.imread(image)[:, :, :-1]
    
        colors, count = np.unique(a.reshape(-1, a.shape[-1]), axis=0, return_counts=True)
        return colors[count.argmax()]

The dominant color should be the background noise. And then subtract this dominant color from the images who has clusters in it:

def substract(image, dominant):
    cells = cv2.imread(image)
    cells = cells[:, :, 2]
    cells = cv2.subtract(cells, dominant)

This code is not working though... :) But besides that, I would like to know if you guys think this is the right direction for this kind of problem?

So in short, I would like to remove the background noise, so that only the information i'm interested in stays in the image. Background noise can very a lot, and I have images where I know that only the background noise is in there and nothing else.

Thank you very much for any hint you can give me! Best regards, Martin

Here is one way in Python/OpenCV/Skimage

 - Read the input
 - Stretch to full dynamic range
 - Apply morphology to clean the image
 - Convert to gray
 - Otsu threshold
 - Get the largest contour
 - Draw the contour outline on the stretched image
 - Extract the contour region from the stretched image and place it on a black background
 - Save the results

Input:

在此处输入图片说明

import cv2
import numpy as np
import skimage.exposure


# read image
img = cv2.imread('dark_image.png')
hh, ww = img.shape[:2]

# stretch dynamic range
stretch = skimage.exposure.rescale_intensity(img, in_range='image', out_range=(0,255))

# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (101,101))
morph = cv2.morphologyEx(stretch, cv2.MORPH_CLOSE, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)

# convert to grayscale
gray = cv2.cvtColor(morph,cv2.COLOR_BGR2GRAY)

# threshold
#thresh = cv2.threshold(gray, 30, 255, cv2.THRESH_BINARY)[1]\
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# get largest contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# draw contour on stretched image
result1 = stretch.copy()
cv2.drawContours(result1, [big_contour], 0, (255,255,255), 1)

# draw region on black background
contour = np.zeros((stretch.shape[:2]), dtype=np.uint8)
result2 = np.zeros_like(stretch)
cv2.drawContours(contour, [big_contour], 0, 255, -1)
result2[contour > 0] = stretch[contour > 0]

# save result
cv2.imwrite("dark_image_stretch.jpg", stretch)
cv2.imwrite("dark_image_morph.jpg", morph)
cv2.imwrite("dark_image_gray.jpg", gray)
cv2.imwrite("dark_image_threshold.jpg", thresh)
cv2.imwrite("dark_image_result1.jpg", result1)
cv2.imwrite("dark_image_result2.jpg", result2)

# view result
cv2.imshow("stretch", stretch)
cv2.imshow("morph", morph)
cv2.imshow("gray", gray)
cv2.imshow("threshold", thresh)
cv2.imshow("result1", result1)
cv2.imshow("result2", result2)
cv2.waitKey(0)
cv2.destroyAllWindows()

Stretch to full dynamic range image:

在此处输入图片说明

Morphology cleaned image:

在此处输入图片说明

Gray image:

在此处输入图片说明

Thresholded image:

在此处输入图片说明

Contour outline on stretched image:

在此处输入图片说明

Contour region from stretched image on black background:

在此处输入图片说明

Here is another variation from my previous solution in Python/OpenCV.

 - Read the input
 - Stretch to full dynamic range
 - Convert to gray
 - Threshold
 - Blur
 - Threshold again
 - Get the largest contour
 - Draw the contour outline on the stretched image
 - Extract the contour region from the stretched image and place it on a black background
 - Save the results

Input:

在此处输入图片说明

  • Read the input
  • Stretch to full dynamic range
  • Apply morphology to clean the image
  • Convert to gray
  • Otsu threshold
  • Get the largest contour
  • Draw the contour outline on the stretched image
  • Extract the contour region from the stretched image and place it on a black background
  • Save the results

Stretched image:

在此处输入图片说明

Gray image:

在此处输入图片说明

Final threshold image:

在此处输入图片说明

Result 1:

在此处输入图片说明

Result 2:

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM