I have a b&w image with 4 groups of points, and I need to transform each group into a single point.
My idea was to find remove points by distance, so if a pixel is maybe 20px away from the other, I would turn remove one and keep the other, and repeat the process until I only got 4 pixels, but it doesn't seem really good for performance.
Is there a better way I can do it?
Thanks
Image:
CodePudding user response:
Your idea is not so far from a workable solution. You can craft a simple algorithm that works as follows:
scan the image in raster order until you meet a white pixel,
from this pixel, erase the whole connected component by seed-filling,
restore the initial pixel and continue the scan to the end of the image.
To erase,
set the current pixel to black,
recurse on all white neighbors (you can consider the four pixels with a common edge and optionally the four ones touching by a corner).
Beware that if the connected components components are large, a stack overflow can occur. Also mind the edge pixels of the image (some neighbors are missing).
This algorithm is pretty efficient, as it will visit the black pixels only once, and the white pixels just twice (once when white, then a second time after erasing).
CodePudding user response:
Here's a possible solution. Basically, I detect the four blobs on your original image, get their bounding boxes and compute their centroids. I use the centroids to flood-fill at this position with black color, and then just draw a pixel in the same position.
However, your image is gigantic and compressed. I resized it (if you don't want to resize it, you can keep the scalePercent at 100), convert it to grayscale and then threshold it - as I said, your image is compressed and some pixels aren't really white, so thresholding gives me a true binary image:
# 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)
Now, let's extract the white blobs, get their bounding boxes, compute the centroids and flood-fill accordingly:
# 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)
This is the output (you have to really zoom-in to see the single pixels, though):


