Here is an example of the kinds of images I'll be dealing with:
(source: csverma at pages.cs.wisc.edu)
There is one bright spot on each ball. I want to locate the coordinates of the centre of the bright spot. How can I do it in Python or Matlab? The problem I'm having right now is that more than one points on the spot has the same (or roughly the same) white colour, but what I need is to find the centre of this 'cluster' of white points.
Also, for the leftmost and rightmost images, how can I find the centre of the whole circular object?
You can simply threshold the image and find the average coordinates of what is remaining. This handles the case when there are multiple values that have the same intensity. When you threshold the image, there will obviously be more than one bright white pixel, so if you want to bring it all together, find the centroid or the average coordinates to determine the centre of all of these white bright pixels. There isn't a need to filter in this particular case. Here's something to go with in MATLAB.
I've read in that image directly, converted to grayscale and cleared off the white border that surrounds each of the images. Next, I split up the image into 5 chunks, threshold the image, find the average coordinates that remain and place a dot on where each centre would be:
im = imread('http://pages.cs.wisc.edu/~csverma/CS766_09/Stereo/callight.jpg');
im = rgb2gray(im);
im = imclearborder(im);
%// Split up images and place into individual cells
split_point = floor(size(im,2) / 5);
images = mat2cell(im, size(im,1), split_point*ones(5,1));
%// Show image to place dots
imshow(im);
hold on;
%// For each image...
for idx = 1 : 5
%// Get image
img = images{idx};
%// Threshold
thresh = img > 200;
%// Find coordinates of thresholded image
[y,x] = find(thresh);
%// Find average
xmean = mean(x);
ymean = mean(y);
%// Place dot at centre
%// Make sure you offset by the right number of columns
plot(xmean + (idx-1)*split_point, ymean, 'r.', 'MarkerSize', 18);
end
I get this:
If you want a Python solution, I recommend using scikit-image combined with numpy and matplotlib for plotting. Here's the above code transcribed in Python. Note that I saved the image referenced by the link manually on disk and named it balls.jpg:
import skimage.io
import skimage.segmentation
import numpy as np
import matplotlib.pyplot as plt
# Read in the image
# Note - intensities are floating point from [0,1]
im = skimage.io.imread('balls.jpg', True)
# Threshold the image first then clear the border
im_clear = skimage.segmentation.clear_border(im > (200.0/255.0))
# Determine where to split up the image
split_point = int(im.shape[1]/5)
# Show image in figure and hold to place dots in
plt.figure()
plt.imshow(np.dstack([im,im,im]))
# For each image...
for idx in range(5):
# Extract sub image
img = im_clear[:,idx*split_point:(idx+1)*split_point]
# Find coordinates of thresholded image
y,x = np.nonzero(img)
# Find average
xmean = x.mean()
ymean = y.mean()
# Plot on figure
plt.plot(xmean + idx*split_point, ymean, 'r.', markersize=14)
# Show image and make sure axis is removed
plt.axis('off')
plt.show()
We get this figure:
Small sidenote
I could have totally skipped the above code and used regionprops (MATLAB link, scikit-image link). You could simply threshold the image, then apply regionprops to find the centroids of each cluster of white pixels, but I figured I'd show you a more manual way so you can appreciate the algorithm and understand it for yourself.
Hope this helps!
Use a 2D convolution and then find the point with the highest intensity. You can apply a concave non-linear function (such as exp) on intensity values before applying the 2d convolution, to intensify the bright spots relative to the dimmer parts of the image. Something like conv2(exp(img),ker)
Related
I have a problem: There is a map where crystals randomly appear and I somehow need to find crystal coordinates(need only one crystal's pixel coordinate), I tried to do it by the color of a specific pixel, but after some time, the pixel shade appears and it does not find it (with axis = 2). Can anyone help I would be very grateful.Maybe some size parameter should be used as it doesn't change. (python)
import cv2
import numpy as np
im = cv2.imread('photos\\temp_3.png')
# Define the blue colour we want to find - remember OpenCV uses BGR ordering
crystal = [0,248,0]
# Get X and Y coordinates of all blue pixels
Y, X = np.where(np.all(im==crystal,axis=2))
print(X, Y)
photo examples:
I am trying to develop an algorithm to determine how symmetrical a picture of a mole would be when processing the image. I have been able to read the picture, convert it from rgb to grayscale, convert grayscale to binary. With the binary image, by using remove small holes and remove small objects, I created a new variable called img_blob that I would then use to analyze how symmetrical the mole would be by using area, perimeter and axes parts from the regionprops function from meas on skimage. To do this, I need to be able to split the blob I create into halves, vertically and horizontally and compare the two sides which is what I need help on. I am trying to do this by mostly using skimage and matplotlib, so nothing from the PIL library. I would like to be able to split my blob into halves which I can assign to a new variable and do the processing of the individual halves from there comparing the top half to the bottom half and the right half to the left half.
So far this is the code I have:
IMG_NAME = "first_mole.png"
img = mpimg.imread(IMG_NAME) #read image
img_gray = rgb2gray(img) #grayscale image
#grayscale image
histogram, bin_edges = np.histogram(img_gray, bins=256, range=(0, 1))
#Binary image
thres = skimage.filters.threshold_otsu(img_gray)
img_binary = (img_gray < thres)
#Removing holes and dots from binary image to better process it
img_binary = morph.remove_small_objects(img_binary, min_size=64)
img_blob = morph.remove_small_holes(img_binary, area_threshold=64)
plt.imshow(img_blob, cmap='gray')
plt.title("Processed Blob")
plt.show()
ang = -things.orientation * 90 / (pi/2) #used in angle parameter to rotate image vertically
#Plotting rotated image
img_blob = transform.rotate(img_blob, ang)
plt.imshow(img_blob, cmap='gray')
plt.title("Rotated Blob")
plt.show()
This is the original photo trying to be processed
This is the binary image of the photo plotted on python
This is the rotated binary image to be used for processing (the one I'm trying to split in halves)
I'm very new to the image processing and object detection. I'd like to extract/identify the position and dimensions of teeth in the following image:
Here's what I've tried so far using OpenCV:
import cv2
import numpy as np
planets = cv2.imread('model.png', 0)
canny = cv2.Canny(planets, 70, 150)
circles = cv2.HoughCircles(canny,cv2.HOUGH_GRADIENT,1,40, param1=10,param2=16,minRadius=10,maxRadius=80)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(planets,(i[0],i[1]),i[2],(255,0,0),2)
# draw the center of the circle
cv2.circle(planets,(i[0],i[1]),2,(255,0,0),3)
cv2.imshow("HoughCirlces", planets)
cv2.waitKey()
cv2.destroyAllWindows()
This is what I get after applying canny filter:
This is the final result:
I don't know where to go from here. I'd like to get all of the teeth identified. How can I do that?
I'd really appreciate any help..
Note that the teeth-structure is more-or-less a parabola (upside-down). If you could somehow guess the parabolic shape that defines the centroids of those blobs (teeth), then your problem could be simplified to a reasonable extent. I have shown a red line that passes through the centers of the teeth.
I would suggest you to approach it as follows:
Binarize your image (background=0, else 1). You could use sklearn.preprocessing.binarize.
Calculate the centroid of all the non-zero pixels. This is the central blue circle in the image. Call this structure_centroid. See this: How to center the nonzero values within 2D numpy array?.
Make polar slices of the entire image, centered at the location of the structure_centroid. I have shown a cartoon image of such polar slices (triangular semi-transparent). Cover complete 360 degrees. See this: polarTransform library.
Determine the position of the centroid of the non-zero pixels for each of these polar slices. See these:
find the distance between a point and a curve python.
Find the minimum distance from a point to a curve.
The array containing these centroids gives you the locus (path) of the average location of the teeth. Call this centroid_path.
Run an elimination/selection algorithm on the circles you were able to detect, that are closest to the centroid_path. Use a threshold distance to drop the outliers.
This should give you a good approximation of the teeth with the circles.
I hope this helps.
I work on MRIs. The problem is that the images are not always centered. In addition, there are often black bands around the patient's body.
I would like to be able to remove the black borders and center the patient's body like this:
I have already tried to determine the edges of the patient's body by reading the pixel table but I haven't come up with anything very conclusive.
In fact my solution works on only 50% of the images... I don't see any other way to do it...
Development environment: Python3.7 + OpenCV3.4
I'm not sure this is the standard or most efficient way to do this, but it seems to work:
# Load image as grayscale (since it's b&w to start with)
im = cv2.imread('im.jpg', cv2.IMREAD_GRAYSCALE)
# Threshold it. I tried a few pixel values, and got something reasonable at min = 5
_,thresh = cv2.threshold(im,5,255,cv2.THRESH_BINARY)
# Find contours:
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# Put all contours together and reshape to (_,2).
# The first "column" will be your x values of your contours, and second will be y values
c = np.vstack(contours).reshape(-1,2)
# Extract the most left, most right, uppermost and lowermost point
xmin = np.min(c[:,0])
ymin = np.min(c[:,1])
xmax = np.max(c[:,0])
ymax = np.max(c[:,1])
# Use those as a guide of where to crop your image
crop = im[ymin:ymax, xmin:xmax]
cv2.imwrite('cropped.jpg', crop)
What you get in the end is this:
There are multiple ways to do this, and this is answer is pretty much computer vision tips and tricks.
If the mass is in the center, and the area outside is always going to be black, you can threshold the image and then find the edge pixels like you already are. I'd add 10 pixels to the border to adjust for variances in the threshold process.
Or if the body is always similarly sized, you can find the centroid of the blob (white area in the threshold image), and then crop a fixed area around it.
Hello and thank you for your help, my problem is this:
I am working on python using satellite images and shapefile, both were original on geographical projection and passed it to pixel so when I plot first the Landsat image is OK, i don't mind the position of axis here, but when i add to the 2D array the shapefile, the areas (in this case), are upside down (I know why because of location of the origin of a image is different from a normal plot.
I try to applied origin='lower' but that is to the final result, when both image and shapefile are added and is not what i want. Also try .reverse(), [::-1] to the array of Y axis before adding to the image but no good result, hope you can help me.
This is the plot of areas in the jungle (of Peru if you are wondering) and blue box is the area i want to show on Landsat (satellite image):
!http://imgur.com/YlM8zhX
This is the area converted to pixels and added to the satellite image, look how is inverted due the location of the origin compared it to the first image
!http://imgur.com/VJN7QYW
thanks for your help.
EDIT: this is kind the code (is really extensive, so the most important is this
#THIS IS THE BORDER AREA EXTRACTED FROM THE SHAPEFILE AND ALREADY CONVERTED TO PIXEL
mos_ext = mos_total[(m0):(m1+1), int(minXY[0]):int(maxXY[0]+1 )]
#I just dilate a little for better visualization
mask3 = cv2.getStructuringElement(cv2.MORPH_RECT, (6, 6))
mos_ext=cv2.dilate(mos_ext, mask3, iterations=1)
#This IS THE LANDSAT IMAGE
im_ls2=mask_bi3.copy()
ADDING THE BORDERS OF AREA (WHAT IS SHOWN ON RED ON THE SECOND IMAGE)
coorde=np.where(mos_ext==255)
im_ls2[coorde[0], coorde[1], 0] = 255
im_ls2[coorde[0], coorde[1], 1] = 0
im_ls2[coorde[0], coorde[1], 2] = 0
fig3, axes3 = plt.subplots(1)
axes3.imshow(im_ls2, cmap='gray', interpolation='nearest')