检测图像是否为负片

Detect if an image is negative

我正在尝试清理一些胸部 X 光数据以提供给 CNN。在我的数据集中,目前有许多图像的骨骼显示为白色(像素值高于背景),如下所示:

而其他人则以比背景更深的颜色显示骨骼,如下所示:

你能告诉我如何标记这两者吗?我没有关于图像的其他外部信息,但可以假设它们大小相同 (

假设它们具有相同的大小(大约 1000x2000)并且第一行像素有超过 1 个不同的值(即不是空白边框),我写了这个简单的代码来比较中间的像素到左上角(可能是背景的一部分)。

if img[0,0] > img[500, 500]: # if background lighter than center
        img = 255 - img # make the image negative

即使从我发布的这些示例中您也可以看出,这种比较并不总是一个好的指标(有时背景周围有光晕或 [500,500] 中的像素可能与背景相似)。有没有更可靠的其他方法来检测这种图像是否为负片?

考虑到数据集中有一些细节和阴影很少的图像,例如

一个可能的解决方案涉及均衡输入图像,然后阈值处理应用固定阈值。我们可以估计 白色像素的数量 并与阈值进行比较以决定是否需要应用校正。

我们来看代码:

# Imports:
import numpy as np
import cv2

# Image path
path = "D://opencvImages//"
fileName = "RPWBn.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Convert RGB to grayscale:
originalGrayscale = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Equalize histogram
grayscaleImage = cv2.equalizeHist(originalGrayscale)

# It might be interesting to you to check out the image equalization:
cv2.imshow("Image Equalized", grayscaleImage)
cv2.waitKey(0)

# Binarize the image with a fixed threshold:
minThresh = 128
_, binaryImage = cv2.threshold(grayscaleImage, minThresh, 255, cv2.THRESH_BINARY)

# Compute the percent of white pixels:
(imageHeight, imageWidth) = binaryImage .shape[:2]
whitePercent = cv2.countNonZero(binaryImage)/(imageHeight * imageWidth)

然后,我们根据阈值检查此值,看看是否必须应用更正。您可以选择校正原始图像和均衡后的图像:

if whitePercent > 0.5:
    print("Correcting images...")
    # Correct the original (unequalized) image:
    originalGrayscale = 255 - originalGrayscale
    cv2.imshow("Correction - Original Image", originalGrayscale)

    # Correct the equalized image:
    grayscaleImage = 255 - grayscaleImage
    cv2.imshow("Correction - Equalized Image", grayscaleImage )
    cv2.waitKey(0)

第二张图片已更正。以下是两种可能结果的图片:

原文倒转:

均衡反转:

现在,除了图像反转,您可能还需要做一些额外的事情 post-processing 来提高原件的亮度和对比度。我们可以使用 CLAHE 方法来实现这一点。让我们 post-process 原始的未均衡图像:

    # Improve the brightness + contrast of the original image via 
    # CLAHE. 
    # Gray to BGR conversion:
    originalGrayscale = cv2.cvtColor(originalGrayscale , cv2.COLOR_GRAY2BGR)

    # Conversion to LAB:
    lab = cv2.cvtColor(originalGrayscale, cv2.COLOR_BGR2LAB)

    # Split the channels:
    l, a, b = cv2.split(lab)

    # Apply CLAHE to L-channel:
    # You might need to fiddle with the parameters:
    clahe = cv2.createCLAHE(clipLimit=7.0, tileGridSize=(1, 1))
    cl = clahe.apply(l)

    # Merge the CLAHE enhanced L-channel with the a and b channel:
    limg = cv2.merge((cl, a, b))

    # Conversion from LAB to BGR:
    final = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
    cv2.imshow("Original Corrected and Enhanced", final)
    cv2.waitKey(0)

这是增强后的图像:

按照Christoph Rackwitz的建议,我用这种方法得到了很好的结果:

  • 可选择规范化图像
  • 从图像中提取角点,更有可能是背景(我只考虑了顶角)
  • 从图像中提取中心
  • 将区域与阈值(图像的固定或平均灰度值)进行比较,得到真值列表
  • 比较高于阈值的像素数量(以百分比表示)以确定背景比图像中心亮还是暗:因此决定反转值。
def invert_if_negative(img):
    img = my_contrast_stretch(img)
    # assuming image has fixed size of (1396, 1676)
    # corners
    top_left  = img[:200, :200].flatten()
    top_right = img[:200, 1250:].flatten()
    # more or less center
    center    = img[1000:1300, 500:800].flatten()
    threshold = 120 # or computed from average
    top_left  = top_left > threshold
    top_right = top_right > threshold
    center    = center > threshold
    perc_white_corners = (sum(top_left) + sum(top_right)) / (len(top_left) + len(top_right))
    perc_white_center = sum(center) / len(center)
    if perc_white_corners > perc_white_center:
        img = 255 - img
    return img
def my_contrast_stretch(img):
    if img.dtype == np.float64:
        img = (img * 255).astype(np.uint8)
    M=np.max(img)
    m=np.min(img)
    res = img - m
    res = res * (255 / (M - m))
    return res.astype(np.uint8)