how to remove the white pixel around the image? - image

How I can corp the image or other methods so that the summation of pixels in the boundary, be less than the hole x and y position. I mean in the images below
the white hole summation x and y of the pixels must be higher in the hole position but as shown in the chart the surrounding of the image especially the x summation of pixels have the higher summation
In this way, I want to found the sum pixels that have high values and illustrate the hole pixels or coordination. In the y-axis, the summation of course so as the below better result than the x one but in some others not, of course, I crop all of 1000 image by below code then after returning the pixel data sum of them and the id and the max values than return the hole is illustrated.
img = Image.open('J:\py.pro\path\picture_1.png').convert('L') # convert image to 8-bit grayscale
if img.mode == "CMYK":
# color profiles can be found at C:\Program Files (x86)\Common Files\Adobe\Color\Profiles\Recommended
img = ImageCms.profileToProfile(img, "USWebCoatedSWOP.icc", "sRGB_Color_Space_Profile.icm", outputMode="RGB")
# PIL image -> OpenCV image; see https://stackoverflow.com/q/14134892/2202732
img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
## (1) Convert to gray, and threshold
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
## (2) Morph-op to remove noise
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10,30))
morphed = cv2.morphologyEx(threshed, cv2.MORPH_CLOSE, kernel)
## (3) Find the max-area contour
cnts = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cnt = sorted(cnts, key=cv2.contourArea)[-1]
## (4) Crop and save it
x,y,w,h = cv2.boundingRect(cnt)
dst = img[y:y+h, x:x+w]
# add border/padding around the cropped image
# dst = cv2.copyMakeBorder(dst, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=[255,255,255])
#cv2.imshow("J:\\py.pro\\path\\pic_1.png", dst)
cv2.imwrite("J:\\py.pro\\path\\pic_1.png", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

How about adding a 1-pixel wide white border on all sides and flood-filling white-coloured pixels with black starting from (0,0) so that the flood-fill flows around the edges filling all white areas at image edges?
I'll demonstrate with magenta for the flood-fill so you can see which pixels are affected:
I actually did that with ImageMagick in Terminal, but you can do just the same with OpenCV:
magick lattice.png -bordercolor white -border 1 -fill magenta -draw 'color 0,0 floodfill' result.png

Related

How to find the closest main color name to RGB color code?

I have need to convert rgb/gbr color code to closest the color name (red, yellow, black). I have found the color code by kmeans algoritms from opencv from central crop image (there are car images with central position on photo and good quality (no dark, light, noisy etc))
https://stackoverflow.com/a/50900494/5558021
and calculate distance to main color code:
black = (-25,-25,-25)
blue_cyan = (255,255,0)
green = (0,255,0)
red = (0,0,255)
white = (255,255,255)
yellow = (0, 255, 255)
colors = [black, blue_cyan, green, red, white, yellow]
diff_c = []
for c in colors:
# dist = sqrt(((color[2]-c[2])*0.3)**2 + ((color[1]-c[1])*0.59)**2 + ((color[0]-c[0])*0.11)**2)
b_ = abs(color[0] - c[0])
g_ = abs(color[1] - c[1])
r_ = abs(color[2] - c[2])
diff_c.append(b_+g_+r_)
color_index = np.argmin(np.array(diff_c))
But my algorithm fails in most times, it's predict blue as black, red as white and etc.
Please recommend a good algorithm for this purpose. Probably I need to convert color in another color space and find the range instead a calculate a single value, or find the mean of color from images?
I have studied other answers on SO, but they also get unsatisfactory result.
Or there is no solution and I should use the CNN approach?

Detect small dots in image

I have grayscale image with dark dots that I can convert to binary (black / white) image.
Sample:
Grayscale input:
B&W image:
I need to find dots in red circles as on
The distance betwen dots is more-or-less uniform if there is no sharp corner.
I have a semi-working solution based on the original grayscale image and the Harris corner detector together with clustering, but it is quite slow and not so straigh-forward.
I have tried Hough transform for circles, but the dots are too small (10x10 px aprox.) to be detected correctly without too much noise.
However, I am able to quite correctly detect the line in grayscale image - see the red line in image. I already use this knowledge and filter dots based on the distance from the line.
However, in some cases this fail. For example the below image is quite problematic - the whick border has a "hole" and the dots are too close, connected to the thick line. I have also false positives from the numbers that are detected as dots.
Do you have any idea for a possible solution, ideally with OpenCV?
Note this is just a sample, the dots may not be on the thin line, but rather separate or the thin line is too bright etc. So the line cannot be used to detect dots.
A potential solution is to use morphological operations with a cv2.MORPH_ELLIPSE kernel to isolate the small dots. The idea is to obtain a binary image with Otsu's threshold then filter out large non-connecting objects using contour area filtering. From were we perform morph open to isolate the dots. Finally we find contours and isolate the dots. Here's the results:
Code
import cv2
import numpy as np
# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Filter out large non-connecting objects
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area < 500:
cv2.drawContours(thresh,[c],0,0,-1)
# Morph open using elliptical shaped kernel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)
# Find circles
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area > 20 and area < 50:
((x, y), r) = cv2.minEnclosingCircle(c)
cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('image', image)
cv2.waitKey()

How to filter white bits in a monochrome image that exceed a specified area?

I have this fancy image of "Brainbow image showing map of neuronal circuits of the mouse cerebral cortex"
and I want to count the number of green flashes so ran k-means on it with 15 clusters and I isolated the two colors that together do the job, but i am left with many green streaks/ tails and edges of yellow flashes.
I was hoping to find some algo that thresholds by area and select only the actual green flashes or maybe another aproach where I would not face this problem. I have used python k-means from sklearn.cluster
You could use color thresholding to isolate the green flashes. The idea is to convert the image to HSV format and define a lower/upper color threshold range. This will give you a binary mask. From here we can do additional processing by performing morphological opening with an elliptical shaped kernel to remove noise and tails. Finally we can find contours and filter using contour area with a defined threshold area to only keep the larger blobs. Here's the result:
Count: 116
Code
import numpy as np
import cv2
# Color threshold
image = cv2.imread('1.jpg')
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([42, 67, 0])
upper = np.array([69, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
# Perform morphological operations
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
# Find contours and filter using contour area
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
count = 0
for c in cnts:
area = cv2.contourArea(c)
if area < 5:
cv2.drawContours(opening,[c], -1, (0,0,0), -1)
else:
count += 1
result = cv2.bitwise_and(original,original,mask=opening)
print('Count: {}'.format(count))
cv2.imshow('mask', mask)
cv2.imshow('opening', opening)
cv2.imshow('result', result)
cv2.waitKey()

Detecting a lego baseplate in an image

I want my code to find the corners of a square lego plate in an image like the one attached.
I also want to find its dimensions, i.e. the number of "blops" in both dimensions (48x48 in the attached image).
I am currently looking at detecting the individual "blops", and the result so far is pretty good: a combination of blur, adaptiveThreshold, findContours and selection based on area finds the contours rendered in the second attached image (coloring is random).
I'm now looking for an algorithm to find the "grid" losely represented by these contours (or their mid-points), but I lack the google fu. Any ideas?
(Suggestions for different approaches are also very welcome.)
(The sample image shows bricks placed in the corners - an algorithm could expect this, if it helps.)
(The sample image has a rather wild background. I'd prefer to cope with that, if possible.)
Update 8 July 2016: I'm trying to write an algorithm that looks for streaks of adjacent contours forming lines. The algo should be able to find a number of these and, from that, deduce the form of the whole plate, even with perspective. Will update if it works...
Update December 2017: The above algorithm sort of worked, although it was a bit too unpredictable. Also I got problems with perspective (adding a "thick" lego brick changes the surface) and color recognition (shadows, camera peculiarities etc). This endeavor is on hold for now. If I resume it I will try with fixed camera positions immediately above the plate and consistent lights.
Here's a potential approach using color thresholding. The idea is to convert the image to HSV format then color threshold using a lower and upper bound with the assumption that the baseplate is in gray. This will give us a mask image. From here we morph open to remove noise, find contours, and sort for the largest contour. Next we obtain the rotated bounding box coordinates and draw this onto a new blank mask. Finally we bitwise-and the mask with the input image to get our result. To find the corner coordinates, we can use cv2.goodFeaturesToTrack() to find the points on the mask. Take a look at how to find accurate corner positions of a distorted rectangle from blurry image in python? and Shi-Tomasi Corner Detector & Good Features to Track for more details
Here's a visualization of each step:
We load the image, convert to HSV format, define a lower/upper bound, and perform color thresholding using cv2.inRange()
import cv2
import numpy as np
# Load image, convert to HSV, and color threshold
image = cv2.imread('1.png')
blank_mask = np.zeros(image.shape, dtype=np.uint8)
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 0, 109])
upper = np.array([179, 36, 255])
mask = cv2.inRange(hsv, lower, upper)
Next we create a rectangular kernel using cv2.getStructuringElement() and perform morphological operations using cv2.morphologyEx(). This step removes small particles of noise.
# Morph open to remove noise
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
From here we find contours on the mask using cv2.findContours() and filter using contour area to obtain the largest contour. We then obtain the rotated boding box coordinates using cv2.minAreaRect() and cv2.boxPoints() then draw this onto a new blank mask with cv2.fillPoly(). This step gives us a "perfect" outer contour of the baseplate. Here's the detected outer contour highlighted in green and the resulting mask.
# Find contours and sort for largest contour
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
# Obtain rotated bounding box and draw onto a blank mask
rect = cv2.minAreaRect(cnts)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image,[box],0,(36,255,12),3)
cv2.fillPoly(blank_mask, [box], (255,255,255))
Finally we bitwise-and the mask with our original input image to obtain our result. Depending on what you need, you can change the background to black or white.
# Bitwise-and mask with input image
blank_mask = cv2.cvtColor(blank_mask, cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(original, original, mask=blank_mask)
# result[blank_mask==0] = (255,255,255) # Color background white
To detect the corner coordinates, we can use cv2.goodFeaturesToTrack(). Here's the detected corners highlighted in purple:
Coordinates:
(91.0, 525.0)
(463.0, 497.0)
(64.0, 152.0)
(436.0, 125.0)
# Detect corners
corners = cv2.goodFeaturesToTrack(blank_mask, maxCorners=4, qualityLevel=0.5, minDistance=150)
for corner in corners:
x,y = corner.ravel()
cv2.circle(image,(x,y),8,(155,20,255),-1)
print("({}, {})".format(x,y))
Full Code
import cv2
import numpy as np
# Load image, convert to HSV, and color threshold
image = cv2.imread('1.png')
blank_mask = np.zeros(image.shape, dtype=np.uint8)
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 0, 109])
upper = np.array([179, 36, 255])
mask = cv2.inRange(hsv, lower, upper)
# Morph open to remove noise
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
# Find contours and sort for largest contour
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
# Obtain rotated bounding box and draw onto a blank mask
rect = cv2.minAreaRect(cnts)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image,[box],0,(36,255,12),3)
cv2.fillPoly(blank_mask, [box], (255,255,255))
# Bitwise-and mask with input image
blank_mask = cv2.cvtColor(blank_mask, cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(original, original, mask=blank_mask)
result[blank_mask==0] = (255,255,255) # Color background white
# Detect corners
corners = cv2.goodFeaturesToTrack(blank_mask, maxCorners=4, qualityLevel=0.5, minDistance=150)
for corner in corners:
x,y = corner.ravel()
cv2.circle(image,(x,y),8,(155,20,255),-1)
print("({}, {})".format(x,y))
cv2.imwrite('mask.png', mask)
cv2.imwrite('opening.png', opening)
cv2.imwrite('blank_mask.png', blank_mask)
cv2.imwrite('image.png', image)
cv2.imwrite('result.png', result)
cv2.waitKey()

how to detect edges in an image having only red object

I have an image, in that image all red objects are detected.
Here's an example with two images:
http://img.weiku.com/waterpicture/2011/10/30/18/road_Traffic_signs_634577283637977297_4.jpg
But when i proceed that image for edge detection method i got the output as only black color. However, I want to detect the edges in that red object.
r=im(:,:,1); g=im(:,:,2); b=im(:,:,3);
diff=imsubtract(r,rgb2gray(im));
bw=im2bw(diff,0.18);
area=bwareaopen(bw,300);
rm=immultiply(area,r); gm=g.*0; bm=b.*0;
image=cat(3,rm,gm,bm);
axes(handles.Image);
imshow(image);
I=image;
Thresholding=im2bw(I);
axes(handles.Image);
imshow(Thresholding)
fontSize=20;
edgeimage=Thresholding;
BW = edge(edgeimage,'canny');
axes(handles.Image);
imshow(BW);
When you apply im2bw you want to use only the red channel of I(i.e the 1st channel). Therefore using this command:
Thresholding =im2bw(I(:,:,1));
for example yields this output:
Just FYI for anyone else that manages to stumble here. The HSV colorspace is better suited for detecting colors over the RGB colorspace. A good example is in gnovice's answer. The main reason for this is that there are colors which can contain full 255 red values but aren't actually red (yellow can be formed from (255,255,0), white from (255,255,255), magenta from (255,0,255), etc).
I modified his code for your purpose below:
cdata = imread('roadsign.jpg');
hsvImage = rgb2hsv(cdata); %# Convert the image to HSV space
hPlane = 360.*hsvImage(:,:,1); %# Get the hue plane scaled from 0 to 360
sPlane = hsvImage(:,:,2); %# Get the saturation plane
bPlane = hsvImage(:,:,3); %# Get the brightness plane
% Must get colors with high brightness and saturation of the red color
redIndex = ((hPlane <= 20) | (hPlane >= 340)) & sPlane >= 0.7 & bPlane >= 0.7;
% Show edges
imshow(edge(redIndex));
Output:

Resources