【问题标题】:Turn group of points into single pixel将一组点变成单个像素
【发布时间】:2022-01-05 20:22:37
【问题描述】:

我有一张包含 4 组点的黑白图像,我需要将每组转换为一个点。

我的想法是按距离查找删除点,​​所以如果一个像素可能与另一个像素相距 20 像素,我会转而删除一个并保留另一个,并重复该过程直到我只有 4 个像素,但它没有看起来性能不太好。

有没有更好的方法可以做到这一点?

谢谢

图片:

【问题讨论】:

  • 你可以使用 findContours 或 connectedComponentsAnalysis
  • 请分享您的实际图像,而不是带有标记轴和网格的绘图!
  • @MarkSetchell 我已经用正确的图像编辑了它!实际上是当你在 jupyter 上绘制它时绘制的 matplotlib 东西
  • 触摸像素组称为连通分量。函数 findContours 将返回所有连接组件的轮廓作为点列表。你可以选择其中之一。如果您想要一个更“中心”的点,您可以计算质心,但在某些情况下它可能位于轮廓之外!
  • 为什么每个人都按字面意思理解并擦除图像的一部分?只需找到组件,计算质心(这里已经足够好了),拥有坐标就足够了。如果确实需要绘制,请在新图像中绘制点。

标签: python numpy opencv image-processing computer-vision


【解决方案1】:

您的想法与可行的解决方案相去甚远。您可以制作一个简单的算法,其工作原理如下:

  • 以光栅顺序扫描图像,直到遇到一个白色像素,

  • 从这个像素,通过种子填充擦除整个连接组件,

  • 恢复初始像素并继续扫描到图像的末尾。

删除,

  • 将当前像素设置为黑色,

  • 递归所有白色邻居(您可以考虑具有公共边缘的四个像素,也可以考虑与角接触的四个像素)。

请注意,如果连接的组件组件很大,可能会发生堆栈溢出。还要注意图像的边缘像素(缺少一些邻居)。

这种算法非常有效,因为它只会访问黑色像素一次,而白色像素只会访问两次(白色时一次,擦除后第二次)。

【讨论】:

  • 我这样做是按半径扫描每个像素并去除白色像素,然后重复 4 次。但是,如果您使用的是基于图形理论的 openCV,似乎有更好的解决方案,我也会尝试一下。谢谢
  • @EnzoDtz:这不是我所描述的。
  • 我可能描述得很糟糕,但我完全明白你所说的。正如我在@stateMachine cmets 上提到的,我认为如果有人通过搜索来到这里,您的解决方案将更适合问题主题,因此我将其标记为解决方案。也许提供一些代码会很好,即使我没有在帖子中询问。非常感谢!
【解决方案2】:

这是一个可能的解决方案。基本上,我检测原始图像上的四个斑点,获取它们的边界框并计算它们的质心。我用质心到flood-fill这个位置用黑色,然后在同一个位置画一个像素。

但是,您的图像是巨大且压缩的。我调整了它的大小(如果你不想调整它的大小,你可以将scalePercent 保留在100),将其转换为grayscale 然后threshold 它 - 正如我所说,你的图像被压缩并且一些像素不是真正的白色,因此阈值处理给了我一个真正的二值图像:

# imports:
import cv2

# Set image path
imagePath = "C://opencvImages//"
imageName = "juPHJ.jpg"

# Read image:
inputImage = cv2.imread(imagePath + imageName)

# Resize percent:
scalePercent = 20

# Calculate new dimensions:
newWidth = int(inputImage.shape[1] * scalePercent / 100)
newHeight = int(inputImage.shape[0] * scalePercent / 100)

# Resize image
inputImage = cv2.resize(inputImage, (newWidth, newHeight))

# Input deep copy
inputCopy = inputImage.copy()

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

# Get binary image via Otsu:
_, binaryImage = cv2.threshold(grayInput, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

现在,让我们提取白色斑点,获取它们的边界框,计算质心并相应地计算flood-fill

# Extract blobs:
contours, _ = cv2.findContours(binaryImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Loop through the blobs, get their bounding boxes:
for i, c in enumerate(contours):

    # Get blob bounding box:
    x, y, w, h = cv2.boundingRect(c)
    # Compute centroid:
    cx = int(x + 0.5 * w)
    cy = int(y + 0.5 * h)

    # Flood-fill at the center:
    fillPosition = (cx, cy)
    fillColor = (0, 0, 0)
    cv2.floodFill(binaryImage, None, fillPosition, fillColor, loDiff=(10, 10, 10), upDiff=(10, 10, 10))

    # Draw a pixel at the center:
    binaryImage[cy, cx] = 255

    # Show new Image:
    cv2.imshow("New Image", binaryImage)
    cv2.waitKey(0)

这是输出(不过,您必须真正放大才能看到单个像素):

【讨论】:

  • 谢谢!这个解决方案真的很酷。我还从 openCV 中找到了返回质心的函数 connectedComponentsWithStats
  • 原来findCountours 解决方案似乎更好,因为使用connectedComponents,它不会删除原始图像中不可见的其他孤立点。这绝对是最适合我的方案,尽管其他提供的解决方案与可能搜索此内容的任何人都更相关。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-08
  • 2021-02-25
  • 2016-12-23
相关资源
最近更新 更多