按空格分割图像

Splitting image by whitespace

我有一个图像,我正试图将其拆分成单独的部分,我已经使用 k-means 聚类成功地创建了图像中对象的掩码。 (我在下面包含了结果和掩码)

然后我尝试裁剪原始图像的每个单独部分并将其保存到新图像中,这可能吗?

import numpy as np
import cv2

path = 'software (1).jpg'
img = cv2.imread(path)

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
twoDimage = img.reshape((-1,3))
twoDimage = np.float32(twoDimage)

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 2
attempts=10

ret,label,center = cv2.kmeans(twoDimage,K,None,criteria,attempts,cv2.KMEANS_PP_CENTERS)
center = np.uint8(center)
res = center[label.flatten()]
result_image = res.reshape((img.shape))


cv2.imwrite('result.jpg',result_image)

Original image

Result of k-means

我的解决方案涉及创建一个 binary 对象掩码,其中所有对象都为白色,背景为黑色。然后我根据面积提取每个对象,从最小到最小。我使用这个“孤立对象”蒙版来分割原始图像中的每个对象。然后我将结果写入磁盘。这些是步骤:

  1. 调整图像大小(您的原始输入是巨大的)
  2. 转换为灰度
  3. 根据面积从最大到最小
  4. 提取每个对象
  5. 创建隔离对象的二进制掩码
  6. 应用一点形态学来增强蒙版
  7. 使用二进制掩码
  8. 掩码原始BGR图像
  9. 应用flood-fill将背景着色为白色
  10. 保存图像到磁盘
  11. 对图像中的所有对象重复该过程

让我们看看代码。通过脚本,我使用了两个辅助函数:writeImagefindBiggestBlob。第一个功能是不言自明的。第二个函数创建二进制输入图像中最大斑点的二进制掩码。这两个功能都在此处介绍:

# Writes an PGN image:
def writeImage(imagePath, inputImage):
    imagePath = imagePath + ".png"
    cv2.imwrite(imagePath, inputImage, [cv2.IMWRITE_PNG_COMPRESSION, 0])
    print("Wrote Image: " + imagePath)


def findBiggestBlob(inputImage):
    # Store a copy of the input image:
    biggestBlob = inputImage.copy()
    # Set initial values for the
    # largest contour:
    largestArea = 0
    largestContourIndex = 0

    # Find the contours on the binary image:
    contours, hierarchy = cv2.findContours(inputImage, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

    # Get the largest contour in the contours list:
    for i, cc in enumerate(contours):
        # Find the area of the contour:
        area = cv2.contourArea(cc)
        # Store the index of the largest contour:
        if area > largestArea:
            largestArea = area
            largestContourIndex = i

    # Once we get the biggest blob, paint it black:
    tempMat = inputImage.copy()
    cv2.drawContours(tempMat, contours, largestContourIndex, (0, 0, 0), -1, 8, hierarchy)
    # Erase smaller blobs:
    biggestBlob = biggestBlob - tempMat

    return biggestBlob

现在,让我们看看主要脚本。让我们读取图像并获取初始二进制掩码:

# Imports
import cv2
import numpy as np

# Read image
imagePath = "D://opencvImages//"
inputImage = cv2.imread(imagePath + "L85Bu.jpg")

# Get image dimensions
originalImageHeight, originalImageWidth = inputImage.shape[:2]

# Resize at a fixed scale:
resizePercent = 30
resizedWidth = int(originalImageWidth * resizePercent / 100)
resizedHeight = int(originalImageHeight * resizePercent / 100)

# resize image
inputImage = cv2.resize(inputImage, (resizedWidth, resizedHeight), interpolation=cv2.INTER_LINEAR)
writeImage(imagePath+"objectInput", inputImage)

# Deep BGR copy:
colorCopy = inputImage.copy()

# Convert BGR to grayscale:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Threshold via Otsu:
_, binaryImage = cv2.threshold(grayscaleImage, 250, 255, cv2.THRESH_BINARY_INV)

这是根据 resizePercent 调整大小 30% 的输入:

这是使用 250 的固定 threshold 创建的二进制掩码:

现在,我要通过 while 循环 运行 这个面具。在每次迭代中,我将提取 最大的 斑点,直到没有斑点为止。每一步都会创建一个新的二进制掩码,其中一次只存在一个对象。这将是隔离原始 (resized) BGR 图像中对象的关键:

# Image counter to write pngs to disk:
imageCounter = 0

# Segmentation flag to stop the processing loop:
segmentObjects = True

while (segmentObjects):

    # Get biggest object on the mask:
    currentBiggest = findBiggestBlob(binaryImage)

    # Use a little bit of morphology to "widen" the mask:
    kernelSize = 3
    opIterations = 2
    morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
    # Perform Dilate:
    binaryMask = cv2.morphologyEx(currentBiggest, cv2.MORPH_DILATE, morphKernel, None, None, opIterations,cv2.BORDER_REFLECT101)

    # Mask the original BGR (resized) image:
    blobMask = cv2.bitwise_and(colorCopy, colorCopy, mask=binaryMask)

    # Flood-fill at the top left corner:
    fillPosition = (0, 0)
    # Use white color:
    fillColor = (255, 255, 255)
    colorTolerance = (0,0,0)
    cv2.floodFill(blobMask, None, fillPosition, fillColor, colorTolerance, colorTolerance)

    # Write file to disk:
    writeImage(imagePath+"object-"+str(imageCounter), blobMask)
    imageCounter+=1

    # Subtract current biggest blob to
    # original binary mask:
    binaryImage = binaryImage - currentBiggest

    # Check for stop condition - all pixels
    # in the binary mask should be black:
    whitePixels = cv2.countNonZero(binaryImage)

    # Compare agaisnt a threshold - 10% of
    # resized dimensions:
    whitePixelThreshold = 0.01 * (resizedWidth * resizedHeight)
    if (whitePixels < whitePixelThreshold):
        segmentObjects = False

这里有一些值得注意的地方。这是为第一个对象创建的第一个隔离掩码:

不错。带有 BGR 图像的简单遮罩即可。但是,如果我应用 dilate 形态学操作,我可以提高蒙版的质量。这将“加宽”斑点,将原始轮廓覆盖几个像素。 (该操作实际上是在 Neighborhood 像素内搜索 maximum 强度像素)。接下来,掩码将生成一个 BGR 图像,其中只有对象斑点和黑色背景。我不想要那个黑色背景,我想要它白色。我在左上角flood-fill得到第一个BGR掩码:

我将每个面具保存在磁盘上的一个新文件中。很酷。现在,退出循环的条件非常简单——当所有的 blob 都被处理后停止。为此,我将当前最大的斑点减去原始二进制白色并计算白色像素的数量。当计数低于某个阈值时(在这种情况下 10% 调整大小的图像)停止循环。

检查每个孤立对象的 gif。每个帧都作为 png 文件保存到磁盘:

  1. 检测外部轮廓。

  2. 每个等高线:

    2.1 创建黑色遮罩图像

    2.2 在mask图像上绘制第i个填充轮廓

    2.3 为第 i 部分创建黑色结果图像

    2.4 使用刚刚创建的蒙版从源图像复制到结果图像

    3.5 保存第i部分的结果图片