Failed to get characters/triangles - opencv

I wanted to read characters/triangles from a bar.
Firstly I applied Otsu with different values to this bar but couldn't get the all characters properly. Also I tried triangle detection but couldn't extract again. The characters' colours are varying. Could someone give another way/algorithm to extract them? Also, is there any way to color sweeping, I mean try all colours then if exist, extract (extract all colored from black&white backgrounded image) ?
ret,im1 = cv2.threshold(crop_img,0,255,cv2.THRESH_OTSU)
The challenges, the last one is the hardest
The best one I got which is unsuccesful:

Your problem is best solved using color separation. You can use the inrange() function for that (docs). This is usually done best in the HSV colorspace. The code below shows how you can do this.
You can use this script to find the value ranges you need to do color separation. It also has a sample image that can help you understand how HSV works.
Result:
Purple only:
Code:
import numpy as np
import cv2
# load image
img = cv2.imread("image.png")
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of HSV-color
lower_val = np.array([0,50,80])
upper_val = np.array([179,255,255])
# purple only
#lower_val = np.array([140,50,80])
#upper_val = np.array([170,255,255])
# Threshold the HSV image to get a mask that holds the markings
mask = cv2.inRange(hsv, lower_val, upper_val)
# create an image of the markings with background excluded
img_masked = cv2.bitwise_and(img,img,mask=mask)
# display image
cv2.imshow("result", img_masked)
cv2.waitKey(0)
cv2.destroyAllWindows()

Related

Separating overlaying colors in opencv

Let's say I have a picture with two colors, but both their colors are overlapping.
Object A is a star, and object B is a rectangle with a star-like hole. They overlap each other.
Is there a way of separating the objects? Kinda like finding the intersection and summing up to their "pure" standards?
I see two ways of doing this: via shape recognition or via color. Don't know which way would be smarter.
First I tried to separate the colors via histogram in grayscale, such as in BATspock's question
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread("origin.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hist = cv2.calcHist([gray],[0],None,[256],[0,256])
colors = np.where(hist>25000)
img_number = 0
for color in colors[0]:
print(color)
split_image = img.copy()
split_image[np.where(gray != color)] = 0
cv2.imwrite(str(img_number)+".jpg",split_image)
img_number+=1
plt.hist(gray.ravel(),256,[0,256])
plt.savefig('plt')
plt.show()
But no success in getting the color of that intersection due to very low Histogram values.
I tried using the example present here, although it renders the same effect I desire, the example just refers to color splitting in RGB. Is there anything similar to this output but choosing the colors instead? Maybe feature recognition? Watershed? I'm lost.

Segmentation problem for tomato leaf images in PlantVillage Dataset

I am trying to do segmentation of leaf images of tomato crops. I want to convert images like following image
to following image with black background
I have reference this code from Github
but it does not do well on this problem, It does something like this
Can anyone suggest me a way to do it ?
The image is separable using the HSV-colorspace. The background has little saturation, so thresholding the saturation removes the gray.
Result:
Code:
import numpy as np
import cv2
# load image
image = cv2.imread('leaf.jpg')
# create hsv
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# set lower and upper color limits
low_val = (0,60,0)
high_val = (179,255,255)
# Threshold the HSV image
mask = cv2.inRange(hsv, low_val,high_val)
# remove noise
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel=np.ones((8,8),dtype=np.uint8))
# apply mask to original image
result = cv2.bitwise_and(image, image,mask=mask)
#show image
cv2.imshow("Result", result)
cv2.imshow("Mask", mask)
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
The problem with your image is the different coloration of the leaf. If you convert the image to grayscale, you will see the problem for the binarization algorithm:
Do you notice the very different brightness of the bottom half and the top half of the leaf? This gives you three mostly uniformly bright areas of the image: The actual background, the top-half leaf and the bottom-half leaf. That's not good for binarization.
However, your problem can be solved by separating your color image into it's respective channels. After separation, you will notice that in the blue channel the leaf looks very uniformly bright:
Which makes sense if we think about the colors we are talking about: Both green and yellow have very small amounts blue in it, if any.
This makes it easy for us to binarize it. For the sake of a clearer image, i first applied smoothing
and then used the iso_data Threshold of ImageJ (you can however use any of the existing automatic thresholding methods available) to create a binary mask:
Because the algorithm has set the leaf to background (black), we have to invert it:
This mask can be further improved by applying binary "fill holes" algorithms:
This mask can be used to crop the original image to extract the leaf:
The quality of the result image could be further improved by eroding the mask a little bit.
For the sake of completeness: You do not have to smooth the image, to get a result. Here is the mask for the unsmoothed image:
To remove the noise, you first apply binary fill holes, then binary closing followed by binary erosion. This will give you:
as a mask.
This will lead to

Normalization image rgb

I have a problem with normalization.
Let me what the problem is and how I attempt to solve it.
I take a three-channel color image, convert it to grayscale and apply uniform or non-uniform quantization and the same thing.
To this image, I should apply the normalization, but I have a problem even if the image and grayscale and always has three channels.
How can I apply normalization having a three-channel image?
Should the min and the max all be in the three channels?
Could someone give me a hand?
The language I am using is processing 2.
P.S.
Can you do the same thing with a color image instead use a grayscale image?
You can convert between the 1-channel and 3-channel representations easily. I'd recommend scikit-image (http://scikit-image.org/).
from skimage.io import imread
from skimage.color import rgb2gray, gray2rgb
rgb_img = imread('path/to/my/image')
gray_img = rgb2gray(rgb_image)
# Now normalize gray image
gray_norm = gray_img / max(gray_img)
# Now convert back
rgb_norm = gray2rgb(gray_norm)
I worked with a similar problem sometime back. One of the good solutions to this was to:
Convert the image from RGB to HSI
Leaving the Hue and Saturation channels unchanged, simply normalize across the Intensity channel
Convert back to RGB
This logic can be applied accross several other image processing tasks, like for example, applying histogram equalization to RGB images.

Get 1 contour per sign through find_contour and retrieve its Humoments in cv2

It is possible obtain only 5 objects (one per sign) by applying find_contour (opencv module) in this image: https://docs.google.com/file/d/0ByS6Z5WRz-h2WHEzNnJucDlRR2s/edit ?
Now I obtain 64 objects
After that I want to retrieve Humoments and make a comparison with other images.
For now i'd try only with the same image a little bit translated, for testing it returns they are the same.
My question I how can I obtain only 5 objects for applying humoments or if there are other solutions to calculate humoments fot the image?
import cv2
im = cv2.imread('Sassatelli 1984 n. 165 mod1.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(imgray, (0,0), 5)
cv2.imshow('Blur', blur)
cv2.waitKey()
th = 20
edges = cv2.Canny(blur, th, th*3)
cv2.imshow('canny',edges)
cv2.waitKey()
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print('objects found')
print(len(contours))
cnt = contours[0]
cv2.drawContours(blur,contours,-1,(0,255,0),3)
cv2.imshow('draw contours',blur)
cv2.waitKey()
moments = cv2.moments(cnt)
Case 1: Problem with saving image in jpg format
When you save a black-and-white-only (ie pixel values 0 and 255 only) image in jpg format, there is lossy compression, which changes the pixel values. If you want to see it, create such an image, save it in jpg, open the saved image and zoom to black-white edge. You can see a pixel value change.
So when you find contours, you expect there is only white objects, but in reality, there is some mid-values also, which is also considered as contours. It increases number of contours.
So to avoid this problem,
Better save images in png or any other lossless format etc.
Apply a threshold, (with a values of 127 or as you like) to make image real binary one before finding contours.
This is much more explained here : What does result of 'list(contour)' denote?
Case 2: Problem with white background
OpenCV findcontours() is designed to find white objects in black background. So if your background is white, it is also treated as one object. So invert the image before finding contours.
Case 3 : Problem with holes in objects
If you have holes in your object, it is also considered as an object. So if you want only external boundary of the objects, use cv2.RETR_EXTERNAL flag for findcontours() function.
Sample Code:
import cv2
import numpy as np
img = cv2.imread('sof.jpg')
gray = cv2.imread('sof.jpg',0)
ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY_INV)
thresholded and inverted image :
Now find the contours, draw it, check the number of contours:
cv2.drawContours(img,contours,-1,(0,255,0),2)
cv2.imshow('img',img),cv2.waitKey(0),cv2.destroyAllWindows()
Result :
NOTE :
Here, I have taken only external contours. If you want to remove internal holes from these objects, you will need to use cv2.RETR_TREE or cv2.RETR_CCOMP flags, and check their hierarchy, and remove them. It is explained in this link : Contours 5 : Hierarchy

Generate Color Histogram around a contoured object

Hey OpenCV/Emgu gurus,
I have an image that I am generating contour for, see below. I am trying to generate a color histogram based pruning of search space of images to look for. How can I get the mask around just the prominent object contour and block out the remaining. So I have a 2 part question:
How do I "invert" the image outside the contour? Floodfill invert, not? I am confused with all the options in OpenCV.
Second, how do I generate a 1-d color histogram from the contoured object in this case the red car to exclude the black background and only generate the color histogram that includes the car.
How would I do that in OpenCV (preferably in Emgu/C# code)?
Perhaps something like this? Done using the Python bindings, but easy to translate the methods to other bindings...
#!/usr/local/bin/python
import cv
import colorsys
# get orginal image
orig = cv.LoadImage('car.jpg')
# show orginal
cv.ShowImage("orig", orig)
# get mask image
maskimg = cv.LoadImage('carcontour.jpg')
# split original image into hue and value
hsv = cv.CreateImage(cv.GetSize(orig),8,3)
hue = cv.CreateImage(cv.GetSize(orig),8,1)
val = cv.CreateImage(cv.GetSize(orig),8,1)
cv.CvtColor(maskimg,hsv,cv.CV_BGR2HSV)
cv.Split(hsv, hue, None, val, None)
# build mask from val image, select values NOT black
mask = cv.CreateImage(cv.GetSize(orig),8,1)
cv.Threshold(val,mask,0,255,cv.CV_THRESH_BINARY)
# show the mask
cv.ShowImage("mask", mask)
# calculate colour (hue) histgram of only masked area
hue_bins = 180
hue_range = [0,180]
hist = cv.CreateHist([hue_bins], cv.CV_HIST_ARRAY, [hue_range], 1)
cv.CalcHist([hue],hist,0,mask)
# create the colour histogram
(_, max_value, _, _) = cv.GetMinMaxHistValue(hist)
histimg = cv.CreateImage((hue_bins*2, 200), 8, 3)
for h in range(hue_bins):
bin_val = cv.QueryHistValue_1D(hist,h)
norm_val = cv.Round((bin_val/max_value)*200)
rgb_val = colorsys.hsv_to_rgb(float(h)/180.0,1.0,1.0)
cv.Rectangle(histimg,(h*2,0),
((h+1)*2-1, norm_val),
cv.RGB(rgb_val[0]*255,rgb_val[1]*255,rgb_val[2]*255),
cv.CV_FILLED)
cv.ShowImage("hist",histimg)
# wait for key press
cv.WaitKey(-1)
This is a little bit clunky finding the mask - I wonder perhaps due to JPEG compression artefacts in the image... If you had the original contour it is easy enough to "render" this to a mask instead.
The example histogram rendering function is also a wee bit basic - but I think it shows the idea (and how the car is predominately red!). Note how OpenCV's interpretation of Hue ranges only from [0-180] degrees.
EDIT: if you want to use the mask to count colours in the original image - edit as so from line 15 downwards:
# split original image into hue
hsv = cv.CreateImage(cv.GetSize(orig),8,3)
hue = cv.CreateImage(cv.GetSize(orig),8,1)
cv.CvtColor(orig,hsv,cv.CV_BGR2HSV)
cv.Split(hsv, hue, None, None, None)
# split mask image into val
val = cv.CreateImage(cv.GetSize(orig),8,1)
cv.CvtColor(maskimg,hsv,cv.CV_BGR2HSV)
cv.Split(hsv, None, None, val, None)
(I think this is more what was intended, as the mask is then derived separately and applied to a completely different image. The histogram is roughly the same in both cases...)

Resources