In this example you can see the Segmentation and a mask showing all the places where the Segmentation has the color blue (0,0,155,255). There is some blue noise, represented as small blue streak just bewteen the green and red area and between the green and orange areas. I would like to remove the blue Segmentation if the segementation has a an area which is smaller than, lets say 50 pixels, and replace it by the color surronding the blue area, without mixing any colors. The end result should only conatin the 6 original colors.
Idealy I would like to perform this process for all 6 colors in the image.
How would I have to go about this, is there any inbuilt function that can do this?
I would apply findContours on the (thresholded) masks, per each color, and collect the segmented representations. Then render each of the colors separately as you've done with the blue mask.
Then I'd use these functions https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html#contour-features
E.g. filtering by: area = cv2.contourArea(cnt) mark the small regions.
That is - iterate the contours and compare if area < ... --> collect:
For each selected small region, you may check the surroundings, which colors are adjacent. That could be done e.g. by sampling some point from the contour (it is a list of coordinates) and scanning in various directions and comparing the colour until finding a different one. That could be helped by finding the extreme points and starting from there, see below:
#... produce masked image for each color, put in masks = [] ...
#... colors = [] ... per each mask/segmented region etc.
for m in masks:
bw = cv2.cvtColor(m,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(bw,127,255,0) # or whatever appropriate params
contours, hierarchy = cv2.findContours(thresh, 1, 2)
streaks = []
for c in contours:
if cv2.contourArea(c) < minSize:
streaks.append(c)
# or process directly, maybe a function here, or could simplify the contour:
# reduce the number of points:
# epsilon = 0.1*cv2.arcLength(cnt,True)
# approx = cv2.approxPolyDP(cnt,epsilon,True)
for x,y in c: # or on the approx or with skipping ... or one point may be enough
# check with offset... Scan in some direction until black/different color or pointPolygonTest() is False etc.
'''
The extreme points could be found which may make the scanning more efficient - c is a contour:
leftmost = tuple(c[c[:,:,].argmin()][0]
rightmost = tuple(c[c[:,:,].argmax()][0]
So having the leftmost coordinate of the contour, the scan should be to the left and for the rightmost - to the right etc. There are bordercases when the small regions are near the border of the image, then the search should iterate over the directions.
Then you can change the color of these small regions to that adjacent one - either in the representation (some class or a tuple) or directly in the image with cv2.fillPoly(...). fillPoly may be used to reconstruct the image of the segmentation.
There could be several adjacent areas with different colours, so if it matters which colour to select, it needs more specifications, e.g. comparing the areas of these adjacent ones and selecting the bigger/smaller one, random etc.