将一组点变成单个像素
Turn group of points into single pixel
我有一张包含 4 组点的黑白图像,我需要将每组转换为一个点。
我的想法是按距离找到移除点,所以如果一个像素可能与另一个相距 20px,我会转而移除一个并保留另一个,并重复该过程直到我只得到 4 个像素,但是它似乎不太适合性能。
我有更好的方法吗?
谢谢
图片:
您的想法离可行的解决方案不远。您可以设计一个简单的算法,其工作原理如下:
按光栅顺序扫描图像,直到遇到白色像素,
从这个像素开始,通过种子填充擦除整个连通分量,
恢复初始像素并继续扫描到图像末尾。
擦除,
将当前像素设置为黑色,
对所有白色邻居进行递归(您可以考虑具有共同边缘的四个像素以及可选的四个接触角的像素)。
请注意,如果连接的组件组件很大,可能会发生堆栈溢出。还要注意图像的边缘像素(一些邻居缺失)。
这个算法非常有效,因为它只会访问黑色像素一次,而白色像素只会访问两次(白色时一次,擦除后第二次)。
这是一个可能的解决方案。基本上,我会检测原始图像上的四个斑点,获取它们的 边界框 并计算它们的 质心 。我用黑色的质心在这个位置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)
这是输出(不过你必须真正放大才能看到单个像素):
我有一张包含 4 组点的黑白图像,我需要将每组转换为一个点。
我的想法是按距离找到移除点,所以如果一个像素可能与另一个相距 20px,我会转而移除一个并保留另一个,并重复该过程直到我只得到 4 个像素,但是它似乎不太适合性能。
我有更好的方法吗?
谢谢
图片:
您的想法离可行的解决方案不远。您可以设计一个简单的算法,其工作原理如下:
按光栅顺序扫描图像,直到遇到白色像素,
从这个像素开始,通过种子填充擦除整个连通分量,
恢复初始像素并继续扫描到图像末尾。
擦除,
将当前像素设置为黑色,
对所有白色邻居进行递归(您可以考虑具有共同边缘的四个像素以及可选的四个接触角的像素)。
请注意,如果连接的组件组件很大,可能会发生堆栈溢出。还要注意图像的边缘像素(一些邻居缺失)。
这个算法非常有效,因为它只会访问黑色像素一次,而白色像素只会访问两次(白色时一次,擦除后第二次)。
这是一个可能的解决方案。基本上,我会检测原始图像上的四个斑点,获取它们的 边界框 并计算它们的 质心 。我用黑色的质心在这个位置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)
这是输出(不过你必须真正放大才能看到单个像素):