I have a Numpy array of colors that is returned when using K-means quantization on an image. After K-means is used, I need to take those centers and calculate the convex hull of the set of color centers.
Essentially I need to find which points/colors don't have enough contrast from another color. I think the answer to this would be to find the points inside the hull (simplices?) that aren't the vertices of the hull.
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from scipy.spatial import ConvexHull
from skimage import color, exposure
from sklearn import cluster
def quantize(pixels, n_colors):
width, height, depth = pixels.shape
reshaped_pixels = np.reshape(pixels, (width * height, depth))
model = cluster.KMeans(n_clusters=n_colors, n_init=100, precompute_distances=True)
labels = model.fit_predict(reshaped_pixels)
centers = model.cluster_centers_
quantized_pixels = np.reshape(centers[labels], (width, height, centers.shape[1]))
return quantized_pixels, centers, labels
def scale_image(img, max_width=100):
img_width, img_height = img.size
ratio = (max_width / float(img_width))
new_height = int((float(img_height)*float(ratio)))
img.thumbnail((max_width, new_height), Image.NEAREST)
return img
# Open image, convert to RGB just in case
img = Image.open('image2.jpg').convert('RGB')
# Scale image to speed up processing
img = scale_image(img, 80)
# Convert to numpy array
np_img_array = np.array(img)
# Convert rgb to lab colorspace
lab_pixels = color.rgb2lab(np_img_array)
# Kmeans quantization
quantized_lab_pixels, centers, labels = quantize(lab_pixels, 16)
# Convert pixels back to rgb
quantized_rgb_pixels = (color.lab2rgb(quantized_lab_pixels)*255).astype('uint8')
# Attempt to use convex hull around cluster centers
hull = ConvexHull(centers)
for simplex in hull.simplices:
plt.plot(centers[simplex, 0], centers[simplex, 1])
plt.scatter(*centers.T, alpha=.5, color='k', marker='v')
for p in centers:
point_is_in_hull = point_in_hull(p, hull)
marker = 'x' if point_is_in_hull else 'd'
color = 'g' if point_is_in_hull else 'm'
plt.scatter(p[0], p[1], marker=marker, color=color)
plt.show()
Update: Here is my rendered plot output. I'm not using the color labels in the plotting in this render.
Here is more so what would be helpful to see. However, the plots aren't what I need... I need to find out which of the cluster centers (colors) make up the vertices of the hull and then pair them back to the vertices labels.
Here is an example of the RGB colors outputted (on the left), and then on the right you would have the final colors, which would be after excluding cluster centers that fall outside of the region.
Related
OK so newbie here that has been working on a set of homework problems with the original post here: How do I make a mask from one image and then transfer it to another?
. The original idea was to take the DAPI image (grey image) and apply it as a mask to the NPM1 (green) image. After implementing the suggested code from HansHirse (thanks!) along with some other code I had been making for the homework problem I finally got a working histogram of all compatible cells in the image. The "compatibility" bit is that any cells touching the border weren't supposed to be counted. However, I still need to find a way to get histograms of each individual cell as well. I've attached the original images from the post too:
To do this, I tried blob_doh and one other method to get segmented regions of each cell but have no idea as to how I can apply these coordinates to an image for the histogram.
PS. The code is a bit messy. I segmented the code such that the blob_doh is near the bottom and the other method is also its own separate piece at the very bottom. Sorry!
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from skimage.feature import blob_dog, blob_log, blob_doh
from skimage.color import rgb2gray
import cv2
import mahotas as mh
import scipy
from scipy import ndimage
import matplotlib.patches as mpatches
from skimage import data
from skimage.filters import threshold_otsu
from skimage.segmentation import clear_border
from skimage.measure import label, regionprops
from skimage.morphology import closing, square
from skimage.color import label2rgb
# Read image into numpy array
image = cv2.imread("NOTREATDAPI.jpg",0)
dna = np.array(image) # must be gray-scale image
plt.show()
# Remove extraneous artifacts from image; set the threshold
dnaf = ndimage.gaussian_filter(dna, 8) #gaussian filter for general image
T = mh.thresholding.otsu(dnaf) # set threshold via mahotas otsu thresholding
theta=np.array(dnaf > T) #setting mask of values in image to calculated otsu threshold
cleared = clear_border(theta) #removes all cells that are in contact with the image border
epsilon = np.array(cleared) #final masked DAPI product
print("DAPI MASK USING GAUSSIAN FILTER AND OTSU THRESHOLDING");
plt.imshow(epsilon)
plt.show()
# Load and reset original images
image = cv2.imread("NOTREATDAPI.jpg",0) #The DAPI Image
image1 = cv2.imread("NOTREATNPM1.jpg",0) #The NPM1 Image
print("Original DAPI Image");plt.imshow(image);plt.show() #The DAPI Image
print("Original NPM1 Image");plt.imshow(image1);plt.show() #The NPM1 Image
# Create an array of bool of same shape as image
maskAboveThreshold = epsilon > 0 #Use mask array from above - include only values above non-masked zeros
print("Final Masked Image of NPM1"); plt.imshow(image1 *
maskAboveThreshold, cmap='gray')
plt.show()
True_NPM1= image1 * maskAboveThreshold # Final masked version of NPM1 set back to grayscale
# Create a mask using the DAPI image and binary thresholding at 25
_, mask = cv2.threshold(True_NPM1, 1, 255, cv2.THRESH_BINARY)
# Do some morphological opening to get rid of small artifacts
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN,
cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15)))
# Calculate the histogram using the NPM1 image and the obtained binary
mask
hist = cv2.calcHist([image1], [0], mask, [256], [0, 256])
# Show bar plot of calculated histogram
plt.bar(np.arange(256), np.squeeze(hist))
plt.show()
# Show mask image
plt.imshow(mask)
plt.show()
#blob_doh way of segmenting the cells ------
import cv2 as cv
from PIL import Image, ImageDraw
image10 = np.array(Image.open("OXALIDAPI.jpg"))
plt.imshow(image10)
#Convert to gaussian image with thresholds
image10 = cv2.imread("OXALIDAPI.jpg",0)
dna = np.array(image10) # gray-scale image
plt.show()
# Remove extraneous artifacts from image; set the threshold
dnaf = ndimage.gaussian_filter(dna, 8) #gaussian filter for general image
T = mh.thresholding.otsu(dnaf) # set threshold via mahotas otsu thresholding
theta=np.array(dnaf > T) #setting mask of values in image to calculated otsu threshold
cleared = clear_border(theta) #removes all cells that are in contact with the image border
image = np.array(cleared) #final masked DAPI product
#print("DAPI MASK USING GAUSSIAN FILTER AND OTSU THRESHOLDING");
plt.imshow(epsilon)
plt.show()
# Convert image to grayscale
image_gray = rgb2gray(image)
plt.imshow(image_gray,cmap="gray")
def plot_blobs(img,blobs):
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.imshow(img, interpolation='nearest')
for blob in blobs:
y, x, r = blob
c = plt.Circle((x, y), r*1.25, color="red", linewidth=1, fill=False)
ax.add_patch(c)
# blob_doh
blobs_doh = blob_doh(image_gray, min_sigma=10, max_sigma=256,
threshold=.025)
plot_blobs(image,blobs_doh)
#get blob coordinates
def filter_blobs(blobs,r_cutoff=5):
new_blobs = []
for b in blobs:
if b[2] > r_cutoff:
new_blobs.append(b)
return new_blobs
new_blobs = filter_blobs(blobs_doh)
#plot_blobs(image,new_blobs)
print(new_blobs)
#Other method of segmenting cells. maybe useful?
yeta = cv2.imread("NOTREATDAPI.jpg",0)
image = np.array(yeta)
# apply threshold
dnaf = ndimage.gaussian_filter(image, 8)
T = mh.thresholding.otsu(dnaf) # set threshold
plt.imshow(dnaf > T)
epsilon=np.array(dnaf > T)
plt.show()
# remove artifacts connected to image border
cleared = clear_border(epsilon)
# label image regions
label_image = label(cleared)
image_label_overlay = label2rgb(label_image, image=image)
fig, ax = plt.subplots(figsize=(6, 6))
ax.imshow(image_label_overlay)
for region in regionprops(label_image):
# take regions with large enough areas
if region.area >= 50:
# draw rectangle around individual cells
minr, minc, maxr, maxc = region.bbox
rect = mpatches.Rectangle((minc, minr), maxc - minc, maxr - minr,
fill=False, edgecolor='red', linewidth=0.5)
ax.add_patch(rect)
#ax.set_axis_off()
#plt.tight_layout()
plt.show()
howzer=np.array(image_label_overlay)
What you are looking for is cv2.connectedComponents. Basically, once you have the binary mask that separate the cells, you try to label each connected component of the mask as one cell:
# I choose OTSU instead of binary, but they are not much different in this case
_, mask = cv2.threshold(dapi, 25, 255, cv2.THRESH_OTSU)
# compute the connected component
labels, markers = cv2.connectedComponents(mask)
# load 2nd image in grayscale
# as your 2nd image is only green/black
npm1 = cv2.imread('npm1.jpg', cv2.IMREAD_GRAYSCALE)
# for you image (and usually), labels[0] is the background
for label in labels[1:]:
# compute the histogram over the entire 256 levels of intensity
hist, bins = np.histogram(npm1[markers==label], bins=range(256))
# do whatever you like to hist
# note that bins=range(256) and hist only have 255 values
plt.bar(bins[1:], hist)
plt.title('cell number: {:}'.format(label))
So for example the histogram of the first and second cells:
And the cell markers are:
I have the intrensic paramaters of my camera, as well as the distortion coefficents, and I know how to calculate out the barrel distortion. - Mainly from this blogpost:
Barrel distortion calculation
However, now I wish to add the barrel distortion like it would be done by the camera itself.
The code for correcting the barrel distortion is the following:
import numpy as np
import cv2
from matplotlib import pyplot as plt
# Define camera matrix K
K = np.array([[1.051e+03,0,0],
[0, 1.0845e+03,0],
[964.4480,544.2625,1.]])
#Matrix was written in matlab style, hence it has to be transposed ...
K = K.transpose()
# Define distortion coefficients d
d = np.array([0.0719,-0.0833,0.0013,-6.1840e-04,0])
# Read an example image and acquire its size
img = cv2.imread("grid.png")
h, w = img.shape[:2]
# Generate new camera matrix from parameters
newcameramatrix, roi = cv2.getOptimalNewCameraMatrix(K, d, (w,h), 0)
# Generate look-up tables for remapping the camera image
mapx, mapy = cv2.initUndistortRectifyMap(K, d, None, newcameramatrix, (w, h), 5)
# Remap the original image to a new image
newimg = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
# Display old and new image
fig, (oldimg_ax, newimg_ax) = plt.subplots(1, 2)
oldimg_ax.imshow(img)
oldimg_ax.set_title('Original image')
newimg_ax.imshow(newimg)
newimg_ax.set_title('Unwarped image')
plt.show()
I tried to simualte the barrel distortion by using the inverse of th K-Matrix, or the transposed K-matrix, as well as multiplicating the d-vector with -1.
I transposed it via:
K = K.transpose()
or inverted it via:
K = np.linalg.inv(K)
Howver, this gave me only a black image. If I do not inverted / transpose it all I just get a negative radial resolution, however I need a positive radial distortion
I am using opencv and python .
I need to detect color of object in image for example given below image , the color of shirt is red.
On this link i found something useful but its detecting image of skin .
http://lokeshdhakar.com/projects/color-thief/
I think i will have to use image contour extraction and then carry out color detection for that .
Getting the dominant colors may be achieved using the following simple approach:
from sklearn.cluster import KMeans
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
img = cv2.imread('red_shirt.jpg')
height, width, dim = img.shape
EDIT: take only the center of the image:
img = img[(height/4):(3*height/4), (width/4):(3*width/4), :]
height, width, dim = img.shape
img_vec = np.reshape(img, [height * width, dim] )
kmeans = KMeans(n_clusters=3)
kmeans.fit( img_vec )
EDIT: count cluster pixels, order clusters by cluster size
unique_l, counts_l = np.unique(kmeans.labels_, return_counts=True)
sort_ix = np.argsort(counts_l)
sort_ix = sort_ix[::-1]
fig = plt.figure()
ax = fig.add_subplot(111)
x_from = 0.05
for cluster_center in kmeans.cluster_centers_[sort_ix]:
ax.add_patch(patches.Rectangle( (x_from, 0.05), 0.29, 0.9, alpha=None,
facecolor='#%02x%02x%02x' % (cluster_center[2], cluster_center[1], cluster_center[0] ) ) )
x_from = x_from + 0.31
plt.show()
You can remove BG and skin pixels with this kind of preprocessing
Load the frame
Convert BGR to HSV
Range the value of pixel
also check out this link Color detection in opencv
I want to use OCR to capture the bowling scores from the monitor at the lances. I had a look at this sudoku solver, as I think its pretty similar - numbers and grids right? It has trouble finding the horizontal lines. Has anyone got any tips for pre-processing this image to make it easier to detect the lines (or numbers!). Also any tips for how to deal with the split (the orange ellipse around some of the 8's int he image)?
So far I have got the outline of the score area and cropped it.
import matplotlib
matplotlib.use('TkAgg')
from skimage import io
import numpy as np
import matplotlib.pyplot as plt
from skimage import measure
from skimage.color import rgb2gray
# import pytesseract
from matplotlib.path import Path
from qhd import *
def polygonArea(poly):
"""
Return area of an unclosed polygon.
:see: https://stackoverflow.com/a/451482
:param poly: (n,2)-array
"""
# we need a plain list for the following operations
if isinstance(poly, np.ndarray):
poly = poly.tolist()
segments = zip(poly, poly[1:] + [poly[0]])
return 0.5 * abs(sum(x0*y1 - x1*y0
for ((x0, y0), (x1, y1)) in segments))
filename = 'good.jpg'
image = io.imread(filename)
image = rgb2gray(image)
# Find contours at a constant value of 0.8
contours = measure.find_contours(image, 0.4)
# Display the image and plot all contours found
fig, ax = plt.subplots()
c = 0
biggest = None
biggest_size = 0
for n, contour in enumerate(contours):
curr_size = polygonArea(contour)
if curr_size > biggest_size:
biggest = contour
biggest_size = curr_size
biggest = qhull2D(biggest)
# Approximate that so we just get a rectangle.
biggest = measure.approximate_polygon(biggest, 500)
# vertices of the cropping polygon
yc = biggest[:,0]
xc = biggest[:,1]
xycrop = np.vstack((xc, yc)).T
# xy coordinates for each pixel in the image
nr, nc = image.shape
ygrid, xgrid = np.mgrid[:nr, :nc]
xypix = np.vstack((xgrid.ravel(), ygrid.ravel())).T
# construct a Path from the vertices
pth = Path(xycrop, closed=False)
# test which pixels fall within the path
mask = pth.contains_points(xypix)
# reshape to the same size as the image
mask = mask.reshape(image.shape)
# create a masked array
masked = np.ma.masked_array(image, ~mask)
# if you want to get rid of the blank space above and below the cropped
# region, use the min and max x, y values of the cropping polygon:
xmin, xmax = int(xc.min()), int(np.ceil(xc.max()))
ymin, ymax = int(yc.min()), int(np.ceil(yc.max()))
trimmed = masked[ymin:ymax, xmin:xmax]
plt.imshow(trimmed, cmap=plt.cm.gray), plt.title('trimmed')
plt.show()
https://imgur.com/LijB85I is an example of how the score is displayed.
I am trying to do a perspective correction of a tilted rectangle ( a credit card), which is tilted in all the 4 directions. I could find its four corners and the respective angles of its tilt but I cannot find the exact location of the coordinates, where it has to be projected. I am using cv2.getPerspectiveTransform to do the transformation.
I have the aspect ratio of the actual card (the non tilted one), I want such coordinates such that the original aspect ratio is preserved. I have tried using a bounding rectangle but this increases the size of the card.
Any help would be appreciated.
Here is the way you need to follow...
For easiness I have resized your image to smaller size,
Compute quadrangle vertices for source image, here I find out manually, you can choose edge detection, hough line etc..
Q1=manual calculation;
Q2=manual calculation;
Q3=manual calculation;
Q4=manual calculation;
Compute quadrangle vertices in the destination image by keeping aspect ratio, here you can to take width of card from above quadrangle vertices of source, and compute height by multiplying with aspect ratio.
// compute the size of the card by keeping aspect ratio.
double ratio=1.6;
double cardH=sqrt((Q3.x-Q2.x)*(Q3.x-Q2.x)+(Q3.y-Q2.y)*(Q3.y-Q2.y)); //Or you can give your own height
double cardW=ratio*cardH;
Rect R(Q1.x,Q1.y,cardW,cardH);
Now you got quadrangle vertices for source and destination, then apply warpPerspective.
You can refer below C++ code,
//Compute quad point for edge
Point Q1=Point2f(90,11);
Point Q2=Point2f(596,135);
Point Q3=Point2f(632,452);
Point Q4=Point2f(90,513);
// compute the size of the card by keeping aspect ratio.
double ratio=1.6;
double cardH=sqrt((Q3.x-Q2.x)*(Q3.x-Q2.x)+(Q3.y-Q2.y)*(Q3.y-Q2.y));//Or you can give your own height
double cardW=ratio*cardH;
Rect R(Q1.x,Q1.y,cardW,cardH);
Point R1=Point2f(R.x,R.y);
Point R2=Point2f(R.x+R.width,R.y);
Point R3=Point2f(Point2f(R.x+R.width,R.y+R.height));
Point R4=Point2f(Point2f(R.x,R.y+R.height));
std::vector<Point2f> quad_pts;
std::vector<Point2f> squre_pts;
quad_pts.push_back(Q1);
quad_pts.push_back(Q2);
quad_pts.push_back(Q3);
quad_pts.push_back(Q4);
squre_pts.push_back(R1);
squre_pts.push_back(R2);
squre_pts.push_back(R3);
squre_pts.push_back(R4);
Mat transmtx = getPerspectiveTransform(quad_pts,squre_pts);
int offsetSize=150;
Mat transformed = Mat::zeros(R.height+offsetSize, R.width+offsetSize, CV_8UC3);
warpPerspective(src, transformed, transmtx, transformed.size());
//rectangle(src, R, Scalar(0,255,0),1,8,0);
line(src,Q1,Q2, Scalar(0,0,255),1,CV_AA,0);
line(src,Q2,Q3, Scalar(0,0,255),1,CV_AA,0);
line(src,Q3,Q4, Scalar(0,0,255),1,CV_AA,0);
line(src,Q4,Q1, Scalar(0,0,255),1,CV_AA,0);
imshow("quadrilateral", transformed);
imshow("src",src);
waitKey();
I have a better solution which is much easy:
The red rectangle on original image and the corners points of the rectangle are source points
We use cv2.getPerspectiveTransform(src, dst) that takes source points and destination points as arguments and returns the transformation matrix which transforms any image to destination image as show in the diagram
We use this transformation matrix in cv2.warpPerspective()
- As you can see results are better. You get a very nice bird view of the image
import cv2
import matplotlib.pyplot as plt
import numpy as np
def unwarp(img, src, dst, testing):
h, w = img.shape[:2]
# use cv2.getPerspectiveTransform() to get M, the transform matrix, and Minv, the inverse
M = cv2.getPerspectiveTransform(src, dst)
# use cv2.warpPerspective() to warp your image to a top-down view
warped = cv2.warpPerspective(img, M, (w, h), flags=cv2.INTER_LINEAR)
if testing:
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
f.subplots_adjust(hspace=.2, wspace=.05)
ax1.imshow(img)
x = [src[0][0], src[2][0], src[3][0], src[1][0], src[0][0]]
y = [src[0][1], src[2][1], src[3][1], src[1][1], src[0][1]]
ax1.plot(x, y, color='red', alpha=0.4, linewidth=3, solid_capstyle='round', zorder=2)
ax1.set_ylim([h, 0])
ax1.set_xlim([0, w])
ax1.set_title('Original Image', fontsize=30)
ax2.imshow(cv2.flip(warped, 1))
ax2.set_title('Unwarped Image', fontsize=30)
plt.show()
else:
return warped, M
im = cv2.imread("so.JPG")
w, h = im.shape[0], im.shape[1]
# We will first manually select the source points
# we will select the destination point which will map the source points in
# original image to destination points in unwarped image
src = np.float32([(20, 1),
(540, 130),
(20, 520),
(570, 450)])
dst = np.float32([(600, 0),
(0, 0),
(600, 531),
(0, 531)])
unwarp(im, src, dst, True)
cv2.imshow("so", im)
cv2.waitKey(0)[![enter image description here][1]][1]
cv2.destroyAllWindows()
I'm writing the answer provided by #Haris in python.
import cv2
import math
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('test.jpg')
rows,cols,ch = img.shape
pts1 = np.float32([[360,50],[2122,470],[2264, 1616],[328,1820]])
ratio=1.6
cardH=math.sqrt((pts1[2][0]-pts1[1][0])*(pts1[2][0]-pts1[1][0])+(pts1[2][1]-pts1[1][1])*(pts1[2][1]-pts1[1][1]))
cardW=ratio*cardH;
pts2 = np.float32([[pts1[0][0],pts1[0][1]], [pts1[0][0]+cardW, pts1[0][1]], [pts1[0][0]+cardW, pts1[0][1]+cardH], [pts1[0][0], pts1[0][1]+cardH]])
M = cv2.getPerspectiveTransform(pts1,pts2)
offsetSize=500
transformed = np.zeros((int(cardW+offsetSize), int(cardH+offsetSize)), dtype=np.uint8);
dst = cv2.warpPerspective(img, M, transformed.shape)
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()