按空格分割图像
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 对象掩码,其中所有对象都为白色,背景为黑色。然后我根据面积提取每个对象,从最小到最小。我使用这个“孤立对象”蒙版来分割原始图像中的每个对象。然后我将结果写入磁盘。这些是步骤:
- 调整图像大小(您的原始输入是巨大的)
- 转换为灰度
- 根据面积从最大到最小
提取每个对象
- 创建隔离对象的二进制掩码
- 应用一点形态学来增强蒙版
- 使用二进制掩码
掩码原始BGR图像
- 应用flood-fill将背景着色为白色
- 保存图像到磁盘
- 对图像中的所有对象重复该过程
让我们看看代码。通过脚本,我使用了两个辅助函数:writeImage
和 findBiggestBlob
。第一个功能是不言自明的。第二个函数创建二进制输入图像中最大斑点的二进制掩码。这两个功能都在此处介绍:
# 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
文件保存到磁盘:
检测外部轮廓。
每个等高线:
2.1 创建黑色遮罩图像
2.2 在mask图像上绘制第i个填充轮廓
2.3 为第 i 部分创建黑色结果图像
2.4 使用刚刚创建的蒙版从源图像复制到结果图像
3.5 保存第i部分的结果图片
我有一个图像,我正试图将其拆分成单独的部分,我已经使用 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 对象掩码,其中所有对象都为白色,背景为黑色。然后我根据面积提取每个对象,从最小到最小。我使用这个“孤立对象”蒙版来分割原始图像中的每个对象。然后我将结果写入磁盘。这些是步骤:
- 调整图像大小(您的原始输入是巨大的)
- 转换为灰度
- 根据面积从最大到最小 提取每个对象
- 创建隔离对象的二进制掩码
- 应用一点形态学来增强蒙版
- 使用二进制掩码 掩码原始BGR图像
- 应用flood-fill将背景着色为白色
- 保存图像到磁盘
- 对图像中的所有对象重复该过程
让我们看看代码。通过脚本,我使用了两个辅助函数:writeImage
和 findBiggestBlob
。第一个功能是不言自明的。第二个函数创建二进制输入图像中最大斑点的二进制掩码。这两个功能都在此处介绍:
# 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
文件保存到磁盘:
检测外部轮廓。
每个等高线:
2.1 创建黑色遮罩图像
2.2 在mask图像上绘制第i个填充轮廓
2.3 为第 i 部分创建黑色结果图像
2.4 使用刚刚创建的蒙版从源图像复制到结果图像
3.5 保存第i部分的结果图片