Discrete logarithmic colorbar in matplotlib - colormap

I want to create a pcolormesh plot with a discrete logarithmic colorbar. Some resolution is lost, but the matching between colors and values seems to be easier (at least for me) if the colormap is discrete.
The code snippet below produces a continuous log colormap with the preferred value range. How can I make it discrete? Here I found how to create a discrete linear colormap, but I couldn't extend it to log scale.
plt.pcolormesh(X,Y,Z,norm=mcolors.LogNorm(vmin=0.01, vmax=100.))
plt.colorbar()
fig = matplotlib.pyplot.gcf()
fig.set_size_inches(4*2.5, 3*2.5)
plt.xlabel("X", horizontalalignment='right', x=1.0)
plt.ylabel("Y", horizontalalignment='right', y=1.0)
plt.tight_layout()

I've managed to create a logarithmic colorbar with even spacing. However, I couldn't figure out how to create a discrete logarithmic colorbar with a logarithmic spacing of the colorbar. I hope this helps!
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
X = np.arange(0, 50)
Y = np.arange(0, 50)
Z = np.random.rand(50, 50)*10
bounds = [0, 0.1, 0.2, 0.3, 0.4, 0.5, .7, .8, .9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
num_c = len(bounds)
cmap = mpl.colormaps['viridis'].resampled(num_c)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig, ax = plt.subplots()
fig.set_size_inches(4*2.5, 3*2.5)
ax.pcolormesh(X, Y, Z, norm=norm, cmap=cmap)
plt.xlabel("X", horizontalalignment='right', x=1.0)
plt.ylabel("Y", horizontalalignment='right', y=1.0)
fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap, norm=norm))
plt.tight_layout()

Related

Measuring an object by reference on an image considering perspective or angle

I made an algorithm to measure an object using a reference, like this:
The reference is the frame and the other (AOL) is the desired object. My code obtained this result:
But the real AOL is 78.6. This is because of the perspective/angle of photograph. So I used in my code Deep Learning and I obtained the the reference and AOL mask, and I made a simple calculation based on the number of pixels for each mask to obtain AOL area (cm²), once I know the actual size of the reference. I tried to correct the angle/perpective based on the reference and I used the reference mask:
I tried to calculate quadrangle vertices based on the reference mask to correct the perspective. I created this code based on this reference Perspective correction in OpenCV using python:
# import the necessary packages
from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
import numpy as np
import imutils
import cv2
import math
import matplotlib.pyplot as plt
# get the single external contours
# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread("./ref/20210702_114527.png") ## Mask Image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)
# find contours in the edge map
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# sort the contours from left-to-right and initialize the
# 'pixels per metric' calibration variable
(cnts, _) = contours.sort_contours(cnts)
pixelsPerMetric = None
orig = image.copy()
box = cv2.minAreaRect(min(cnts, key=cv2.contourArea))
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="int")
# order the points in the contour such that they appear
# in top-left, top-right, bottom-right, and bottom-left
# order, then draw the outline of the rotated bounding
# box
box = perspective.order_points(box)
cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)
# loop over the original points and draw them
for (x, y) in box:
cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1)
print('Box: ',box)
cv2.imshow('Orig', orig)
img = cv2.imread("./meat/sair/20210702_114527.jpg") #original image
rows,cols,ch = img.shape
#pts1 = np.float32([[185,9],[304,80],[290, 134],[163,64]]) #ficou legal 6e.jpg
### Coletando os pontos
pts1 = np.float32(box)
### Draw the vertices on the original image
for (x, y) in pts1:
cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
ratio= 1.6
moldH=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]))
moldW=ratio*moldH
pts2 = np.float32([[pts1[0][0],pts1[0][1]], [pts1[0][0]+moldW, pts1[0][1]], [pts1[0][0]+moldW, pts1[0][1]+moldH], [pts1[0][0], pts1[0][1]+moldH]])
#print('cardH: ',cardH,cardW)
M = cv2.getPerspectiveTransform(pts1,pts2)
print('M:', M)
print('pts1:', pts1)
print('pts2:', pts2)
offsetSize= 320
transformed = np.zeros((int(moldW+offsetSize), int(moldH+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()
And I got this:
No perspective correction. I have a lot of information like vertices, the correct size of the reference. Is it possible to do a mathematical correction based on quadrangle vertices, like a regression? Not necessarily correcting the image directly, unless there is a good method to correct the perspective image. Or maybe a different approach based on math? Thanks for your patience.
For Christoph:
This is the result position too:
pts1: [[ 9. 51.]
[392. 56.]
[388. 336.]
[ 5. 331.]]

Pyorch: Applying a batch of filters (kernels) on one single picture using conv2d

I have a batch of filters, i.e., w, whose size is torch.Size([64, 3, 7, 7]) as follows:
Also, I have a picture p from Imagenet as follows:
How can I apply the filters to the picture and get a grid of 64x64 where each cell contains the same picture on which a different filter has been applied? I would like to make the grid using torchvision.utils.make_grid but do not know how?
My try
y = F.conv2d(p, w)
The size of y is torch.Size([1, 64, 250, 250]) which does not make sense to me.
Each of your filters has size [3, 7, 7], so they would take an RGB image and produce a single channel output which is stacked in the channel dimension so your output [1, 64, H, W] makes perfect sense.
To visualize these filters:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torchvision import transforms
from PIL import Image
import matplotlib.pyplot as plt
torch.random.manual_seed(42)
transform = transforms.Compose([transforms.ToTensor()])
img = transform(Image.open('dog.jpg')).unsqueeze(0)
print('Image size: ', img.shape)
filters = torch.randn(64, 3, 7, 7)
out = F.conv2d(img, filters)
print('Output size: ', out.shape)
list_of_images = [out[:,i] for i in range(64)]
grid = torchvision.utils.make_grid(list_of_images, normalize=True)
plt.imshow(grid.numpy().transpose(1,2,0))
plt.show()
This is a more accurate representation of the output. It is however not very attractive -- we can obtain the colored version by processing each color channel independently. (The grayscale version can be obtained by summing over the color channels)
color_out = []
for i in range(3):
color_out.append(F.conv2d(img[:,i:i+1], filters[:,i:i+1]))
out = torch.stack(color_out, 2)
print('Output size: ', out.shape)
list_of_images = [out[0,i] for i in range(64)]
print(list_of_images[0].shape)
grid = torchvision.utils.make_grid(list_of_images, normalize=True)
plt.imshow(grid.numpy().transpose(1,2,0))
plt.show()

Best learning algorithms concentric and not linearly separable data

Below are two scatter plots. The first one is for data points that have values of x and y, and I would like to know if there is a clustering algorithm that will automatically recognize that there are two clusters. They are concentric and not linearly separable. K-means is not right for several reasons. The other plot is similar but it has x, y and color values, and I would like to know what learning algorithm would be best at classifying or predicting the correct color from the values of x and y.
I got good classifier results for this problem using the sklearn MLPClassifier algorithm. Here is the scatter and contour plots:
Detailed code at: https://www.linkedin.com/pulse/couple-scikit-learn-classifiers-peter-thorsteinson. The simplified code below shows how it works:
import math
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
# Generate the artificial data set and display the resulting scatter plot
x = []
y = []
z = []
for i in range(500):
rand = np.random.uniform(0.0, 2*math.pi)
randx = np.random.normal(0.0, 30.0)
randy = np.random.normal(0.0, 30.0)
if np.random.random() > 0.5:
z.append(0)
x.append(100*math.cos(rand) + randx)
y.append(100*math.sin(rand) + randy)
else:
z.append(1)
x.append(300*math.cos(rand) + randx)
y.append(300*math.sin(rand) + randy)
plt.axis('equal')
plt.axis([-500, 500, -500, 500])
plt.scatter(x, y, c=z)
plt.show()
# Run the MLPClassifier algorithm on the training data
XY = pd.DataFrame({'x': x, 'y': y})
print(XY.head())
Z = pd.DataFrame({'z': z})
print(Z.head())
XY_train, XY_test, Z_train, Z_test = train_test_split(XY, Z, test_size = 0.20)
mlp = MLPClassifier(hidden_layer_sizes=(10, 10, 10), max_iter=1000)
mlp.fit(XY_train, Z_train.values.ravel())
# Make predictions on the test data and display resulting scatter plot
predictions = mlp.predict(XY_test)
print(confusion_matrix(Z_test,predictions))
print(classification_report(Z_test,predictions))
plt.axis('equal')
plt.axis([-500, 500, -500, 500])
plt.scatter(XY_test.x, XY_test.y, c=predictions)
plt.show()

Getting the coordinates for the "hottest areas" on a numpy heatmap

I've got a heat map numpy array with shape (600,400). The heatmap represents probabilities of detection. In my case, the probability of face detections in an image. My goal is to take this heatmap and get the coordinates (X and Y) where the highest probability occurs.
I've solved this for the case of a single face. The code for that is the following:
face_location = np.unravel_index(heatmap.argmax(), heatmap.shape)
print("Face location: " + str(face_location))
But in some cases there are multiple faces. I don't know how to adjust the algorithm to return multiple "hottest area". The issue is that any one hot area will be surrounded by gradually less hot areas. And so it's possible that after the hottest area, the next top 10 will all be right beside the initial point.
How can I adjust the algorithm to look for multiple hot areas? It's ok to assume that they won't be right beside each other.
heatmap = [[ 2.00299415e-04 2.03753079e-04 8.17560707e-04 ..., 2.23556344e-04
1.98958180e-04 9.92935777e-01]
[ 2.00642273e-04 2.04473894e-04 8.19963054e-04 ..., 2.24148811e-04
1.99438742e-04 9.92921114e-01]
[ 2.01056406e-04 2.05344462e-04 8.22864589e-04 ..., 2.24864416e-04
2.00019145e-04 9.92903233e-01]
...,
[ 7.28193991e-05 -2.73474743e-05 2.95096161e-05 ..., 5.96550672e-05
1.98282614e-05 9.99637246e-01]
[ 7.34055429e-05 -2.72389279e-05 3.02382941e-05 ..., 5.98490733e-05
2.04356711e-05 9.99619305e-01]
[ 7.37556256e-05 -2.71740992e-05 3.06735128e-05 ..., 5.99649393e-05
2.07984649e-05 9.99608397e-01]]
Perhaps consider using a mask array with a threshold probability defining the hot areas?
In [29]: threshold_probability = 0.8
In [30]: prng = np.random.RandomState(42)
In [31]: heatmap = prng.rand(600, 400)
In [32]: heatmap
Out[32]:
array([[ 0.37454012, 0.95071431, 0.73199394, ..., 0.42899403,
0.75087107, 0.75454287],
[ 0.10312387, 0.90255291, 0.50525237, ..., 0.56513318,
0.69665082, 0.92249938],
[ 0.70723863, 0.15253904, 0.57628836, ..., 0.96887786,
0.74965183, 0.13008624],
...,
[ 0.77669933, 0.98757844, 0.72686576, ..., 0.149866 ,
0.6685433 , 0.90248875],
[ 0.116007 , 0.96352904, 0.33109138, ..., 0.85776718,
0.88838363, 0.00901272],
[ 0.30810176, 0.43190563, 0.60935151, ..., 0.07498895,
0.60716006, 0.31712892]])
In [33]: hottest_areas = np.ma.MaskedArray(heatmap, heatmap < threshold_probability)
In [34]: X, Y = hottest_areas.nonzero()
In [35]: X
Out[35]: array([ 0, 0, 0, ..., 599, 599, 599])
In [36]: Y
Out[36]: array([ 1, 7, 11, ..., 376, 388, 394])
The result is a tuple containing the x and y coords of the values for which the boolean condition defining the mask is False (i.e., areas for which the probability of face is higher than threshold).
If you want to go with a threshold like davidrpugh proposed I have a different approach to propose.
Instead of finding the non zero elements, just find the connexe components of your binary image.
import numpy as np
from scipy.ndimage.measurements import label
from skimage.measure import regionprops
heatmap = np.random.rand(100, 25)
thresh = 0.9
bw = np.array(heatmap)
bw[bw < thresh] = 0
img_cc, nb_cc = label(bw)
cc = regionprops(img_cc)
face_location = np.array([c.centroid for c in cc])
import matplotlib.pyplot as plt
plt.figure()
plt.imshow(heatmap)
plt.plot(face_location[:, 1], face_location[:, 0], 'r*')
plt.figure()
plt.imshow(img_cc)
plt.plot(face_location[:, 1], face_location[:, 0], 'r*')
plt.show()
The face location are here defined by the centers of the connexe components but you can look for the maximum of each region in the image instead.

Non-linear axes for imshow in matplotlib

I am generating 2D arrays on log-spaced axes (for instance, the x pixel coordinates are generated using logspace(log10(0.95), log10(2.08), n).
I want to display the image using a plain old imshow, in its native resolution and scaling (I don't need to stretch it; the data itself is already log scaled), but I want to add ticks, labels, lines that are in the correct place on the log axes. How do I do this?
Ideally I could just use commands line axvline(1.5) and the line would be in the correct place (58% from the left), but if the only way is to manually translate between logscale coordinates and image coordinates, that's ok, too.
For linear axes, using extents= in the call to imshow does what I want, but I don't see a way to do the same thing with a log axis.
Example:
from matplotlib.colors import LogNorm
x = logspace(log10(10), log10(1000), 5)
imshow(vstack((x,x)), extent=[10, 1000, 0, 100], cmap='gray', norm=LogNorm(), interpolation='nearest')
axvline(100, color='red')
This example does not work, because extent= only applies to linear scales, so when you do axvline at 100, it does not appear in the center. I'd like the x axis to show 10, 100, 1000, and axvline(100) to put a line in the center at the 100 point, while the pixels remain equally spaced.
In my view, it is better to use pcolor and regular (non-converted) x and y values. pcolor gives you more flexibility and regular x and y axis are less confusing.
import pylab as plt
import numpy as np
from matplotlib.colors import LogNorm
from matplotlib.ticker import LogFormatterMathtext
x=np.logspace(1, 3, 6)
y=np.logspace(0, 2,3)
X,Y=np.meshgrid(x,y)
z = np.logspace(np.log10(10), np.log10(1000), 5)
Z=np.vstack((z,z))
im = plt.pcolor(X,Y,Z, cmap='gray', norm=LogNorm())
plt.axvline(100, color='red')
plt.xscale('log')
plt.yscale('log')
plt.colorbar(im, orientation='horizontal',format=LogFormatterMathtext())
plt.show()
As pcolor is slow, a faster solution is to use pcolormesh instead.
im = plt.pcolormesh(X,Y,Z, cmap='gray', norm=LogNorm())
Actually, it works fine. I'm confused.
Previously I was getting errors about "Images are not supported on non-linear axes" which is why I asked this question. But now when I try it, it works:
import matplotlib.pyplot as plt
import numpy as np
x = np.logspace(1, 3, 5)
y = np.linspace(0, 2, 3)
z = np.linspace(0, 1, 4)
Z = np.vstack((z, z))
plt.imshow(Z, extent=[10, 1000, 0, 1], cmap='gray')
plt.xscale('log')
plt.axvline(100, color='red')
plt.show()
This is better than pcolor() and pcolormesh() because
it's not insanely slow and
is interpolated nicely without misleading artifacts when the image is not shown at native resolution.
To display imshow with abscisse log scale:
ax = fig.add_subplot(nrow, ncol, i+1)
ax.set_xscale('log')

Resources