Feature extraction and take color histogram - python

I am working on an image processing feature extraction. I have a photo of a bird in which I have to extract bird area and tell what color the bird has. I used canny feature extraction method to get the edges of a bird.
How to extract only bird area and make the background to blue color?
openCv solution should also be fine.
import skimage
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import os
filename = os.path.join(os.getcwd(),'image\image_bird.jpeg')
from skimage import io
bird =io.imread(filename,as_grey=True)
plt.imshow(bird)
from skimage import feature
edges = feature.canny(bird,sigma=1)
plt.imshow(edges )
Actual bird image can be taken from bird link

Identify the edges of your image
Binarize the image via automatic thresholding
Use contour detection to identify black regions which are inside a white region and merge them with the white region. (Mockup, image may slightly vary)
Use the created image as mask to color the background and color it
This can be done by simply setting each background pixel (black) to its respective color.
As you can see, the approach is far from perfect, but should give you a general idea about how to accomplish your task. The final image quality might be improved by slightly eroding the map to tighten it to the contours of the bird. You then also use the mask to calculate your color histogram by only taking foreground pixels into account.
Edit: Look here:
Eroded mask
Final image

According to this article https://www.pyimagesearch.com/2016/04/11/finding-extreme-points-in-contours-with-opencv/
and this question CV - Extract differences between two images
I wrote some python code as below. As my predecessor said it is also far from perfect. The main disadvantages of this code are constants value to set manually: minThres (50), maxThres(100), dilate iteration count and erode iteration count.
import cv2
import numpy as np
windowName = "Edges"
pictureRaw = cv2.imread("bird.jpg")
## set to gray
pictureGray = cv2.cvtColor(pictureRaw, cv2.COLOR_BGR2GRAY)
## blur
pictureGaussian = cv2.GaussianBlur(pictureGray, (7,7), 0)
## canny edge detector - you must specify threshold values
pictureCanny = cv2.Canny(pictureGaussian, 50, 100)
## perform a series of erosions + dilations to remove any small regions of noise
pictureDilate = cv2.dilate(pictureCanny, None, iterations=20)
pictureErode = cv2.erode(pictureDilate, None, iterations=5)
## find the nozero regions in the erode
imask2 = pictureErode>0
## create a Mat like pictureRaw
canvas = np.full_like(pictureRaw, np.array([255,0,0]), dtype=np.uint8)
## set mask
canvas[imask2] = pictureRaw[imask2]
cv2.imwrite("result.png", canvas)

Related

How to determine how many distinct curves are in an image using Python?

I am trying to write an algorithm to systematically determine how many different "curves" are in an image. Example Image. I'm specifically interested in the white lines here, so I've used a color threshold to mask the rest of the image and only get the white pixels. These lines represent a path run by a player (wide receivers in the NFL), so I'm interested in the x and y coordinates that the path represents - and each "curve" represents a different path that the player took (or "route"). All curves should start on or behind the blue line.
However, while I can get just the white pixels, I can't figure out how to systematically identify the separate curves. In this example image, there are 8 white curves (or routes) present. I've identified those curves in this image. I tried edge detection, and then using scipy ndimage to get the number of connected components, but because the curves overlap it counts them as connected and only gives me 3 labeled components for this image as opposed to eight. Here's what the edge detection output looks like. Is there a better way to go about this? Here is my sample code.
import cv2
from skimage.morphology import skeletonize
import numpy as np
from scipy import ndimage
#Read in image
image = cv2.imread('example_image.jpeg')
#Color boundary to get white pixels
lower_white = np.array([230, 230, 230])
upper_white = np.array([255, 255, 255])
#mask image for white pixels
mask = cv2.inRange(image, lower_white, upper_white)
c_pixels = cv2.bitwise_and(image, image, mask=mask)
#make pixels from 0 to 1 form to use in skeletonize
c_pixels = c_pixels.clip(0,1)
ske_c = skeletonize(c_pixels[:,:,1]).astype(np.uint8)
#Edge Detection
inputImage =ske_c*255
edges = cv2.Canny(inputImage,100,200,apertureSize = 7)
#Show edges
cv2.imshow('edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
#Find number of components
# smooth the image (to remove small objects); set the threshold
edgesf = ndimage.gaussian_filter(edges, 1)
T = 50 # set threshold by hand to avoid installing `mahotas` or
# `scipy.stsci.image` dependencies that have threshold() functions
# find connected components
labeled, nr_objects = ndimage.label(edgesf > T) # `dna[:,:,0]>T` for red-dot case
print("Number of objects is %d " % nr_objects)

Difficulty in detected ellipses in image

I am trying to detect ellipses in some images.
After some functions I got this edges map:
I tried using Hough transform to detect ellipses, but this transform has very high complexity, so my computer didn't finish running the transform command even after 5 hours(!).
I also tried doing connected components and got this:
In last case I also tried continue and binarized the image.
In all cases I am stuck in these steps, and have no idea how continue from here.
My mission is detect tomatoes in the image. I am approaching this by trying to detect circles and ellipses and find the radius (or average radius in ellipses case) for each one.
edited:
I add my code for the first method (the result is edge map from above):
img = cv2.imread(r'../images/assorted_tomatoes.jpg')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgAfterLight=lightreduce(img)
imgAfterGamma=gamma_correctiom(imgAfterLight,0.8)
th2 = 255 - cv2.adaptiveThreshold(imgAfterGamma,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,5,3)
median2 = cv2.medianBlur(th2,3)
where median2 is the result of shown above in edge map
and the code for connected components:
import scipy
from scipy import ndimage
import matplotlib.pyplot as plt
import cv2
import numpy as np
fname=r'../images/assorted_tomatoes.jpg'
blur_radius = 1.0
threshold = 50
img = scipy.misc.imread(fname) # gray-scale image
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(img.shape)
# smooth the image (to remove small objects)
imgf = ndimage.gaussian_filter(gray_img, blur_radius)
threshold = 80
# find connected components
labeled, nr_objects = ndimage.label(imgf > threshold)
where labeled is the result above
another edit:
this is the input image:
Input
The problem is that after edge detection, there are a lot of unnecessary edges in sub regions that disturbing for make smooth edge map
To me this looks like a classic problem for the watershed algorithm. It is designed for segmenting out touching objects like the tomatoes. My example is in Matlab (I'm on the wrong computer today) but it should translate to python easily. First convert to greyscale as you do and then invert the images
I=rgb2gray(img)
I2=imcomplement(I)
The image as is will over segment, so we remove minima that are too shallow. This can be done with the h-minima transform
I3=imhmin(I2,50);
You might need to play with the 50 value which is the height threshold for suppressing shallow minima. Now run the watershed algorithm and we get the following result.
L=watershed(I3);
The results are not perfect. It needs additional logic to remove some of the small regions, but it will give a reasonable estimate. The watershed and h-minima are contained in the skimage.morphology package in python.

Python: How to keep region inside canny close edge's area

I'm using canny algorithm to find the edges.
Next, I want to keep the region inside the closed curves.
My code sample is:
import cv2
import numpy as np
from matplotlib import pyplot as plt
import scipy.ndimage as nd
from skimage.morphology import watershed
from skimage.filters import sobel
img1 = cv2.imread('coins.jpg')
img = cv2.imread('coins.jpg',0)
edges= cv2.Canny(img,120,200)
markers = np.zeros_like(img)
markers[edges<50] = 0
markers[edges==255] = 1
img1[markers == 1] = [0,0,255]
img1[markers == 0] = [255,255,255]
cv2.imshow('Original', img)
cv2.imshow('Canny', img1)
#Wait for user to press a key
cv2.waitKey(0)
My output image is
I want to show the original pixels values inside the coins. Is that possible?
I suggest you use an union-find structure to get the connected components of white pixels of your img1. (You might want to find the details of this algorithm on Wikipedia : https://en.wikipedia.org/wiki/Disjoint-set_data_structure).
Once you have the connected components, my best idea is to consider the conected components that do not contain any point on the border of your picture (they should correspond to the interior of your coins) and color them in the color of img.
Sure, you may have some kind of triangles between your coins that will still be colored, but you could remove the corresponding connected components by hand.
Not really. The coin outlines are not continuous so that any kind of filling will leak.
You can repair the edges by some form of morphological processing (erosion), but this will bring the coins in contact and create unreachable regions between them.
As a fallback solution, you can try a Hough circle detector and mask inside the disks.

OpenCV Dynamic segmentation method for blurred or degraded image

I have implemented segmentation for given images, but images may vary based on different color. How can I separate background from the foreground where foreground contains hollow circle/filled circle only. My goal is to find threshold value automatically based on color of image.
[Sample images][1]
import numpy as np
import cv2
import os
image =cv2.imread("cropped2/pnr6.jpg")
img = image.copy()
MARKER_LOWER_BOUND = ( 0, 0, 0)
MARKER_UPPER_BOUND = (255, 255, 25)
marker_seg_mask = cv2.inRange(img, MARKER_LOWER_BOUND, MARKER_UPPER_BOUND)
cv2.imshow("thresold.jpg",marker_seg_mask)
Look at the cv2.adaptiveThreshold() function, which should do what you need.
From the docs:
Adaptive Thresholding
In the previous section, we used a global value as threshold value.
But it may not be good in all the conditions where image has different
lighting conditions in different areas. In that case, we go for
adaptive thresholding. In this, the algorithm calculate the threshold
for a small regions of the image. So we get different thresholds for
different regions of the same image and it gives us better results for
images with varying illumination.

Approximate polygons from image (map)

So I have this map:
I've already done some filtering and now I have the following image:
Finally, I want to find the coordinates for each of the polygons in the image, but using the find_contours function is not giving me good results, as you can see on the following image.
On the left side, you can see all of the contours (polygons) found (one color for each), and on the right side, it's an example of a polygon it has found (which is clearly wrong).
Having the black and white image, how can I find the coordinates for each of the polygons in it?
So when I downloaded the image, I got a very disjoint kind of image. So what I did was I dilated it.
import numpy as np
from skimage import io, measure, morphology
img = io.imread('img.png', as_grey=True)
img = morphology.binary_dilation(img, selem=np.ones((5,5)))
Then what I did was I inverted the white line with black and the area as white with this
img_inverted = np.invert(img)
Once you have the inverted image, Label it for easy processing.
img_inverted_labeled = measure.label(img_inverted)
n_lbls = np.unique(img_inverted_labeled)[1:]
Now append each part's polygon.
pols = []
for i in n_lbls:
img_part = (img_inverted_labeled == i)
pols.append(measure.find_contours(img_part, level=0)[0])
Hope this helps.

Categories