Actually plotting this image using plt.imshow and np.masked to plot the figure in blue. I would like to remove the inner part of the figure and just leaving the border. Tried with fill=False but that's not an arg for plt.imshow.
Code:
#img comes from a raster using GDAL
img = np.dstack((band1, band2, band3))
max = img.max()
img = img / max
one = f.add_subplot(1, 2, 1)
one.set_title('Polygon ID: '+index)
plt.imshow(img, cmap=plt.cm.binary)
plt.imshow(masked_polygon, 'jet', interpolation='none', alpha=0.3)
plt.axis('off')
Result of plot:
Related
My goal is to show a binary image and then plot the boundary contours as lines overlaying the image. If I do this and export the result as a PDF, I see a progressively worsening misalignment between the image and contours as one moves across the image from bottom left. So it seems like there is a multiplicative error in the position of either the background image or the contours.
I think the issue is caused by the PDF renderer. If I output the result in PNG with a very high DPI, I can remove the problem, but I would prefer PDF for other reasons. Does anyone know if there is a setting I can change to make the PDF render correctly?
Here is an example and the resulting image. You can see that the bottom left corner has good alignment between image and contour and the top right is the worst.
import numpy as np
import matplotlib.pyplot as plt
import cv2
# Make a test image
img = np.zeros((100,100), dtype=np.uint8)
img[20:99,1:80] = 1
img = np.matlib.repmat(img, 9, 6)
# Extract contours
cntrs, hier = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# Make overlay
fig = plt.figure(figsize=(6,9), dpi=300)
ax = fig.add_subplot()
ax.imshow(img, interpolation='none', cmap=plt.cm.gray)
for cntr in cntrs:
x = np.append(cntr[:, 0, 0], cntr[0, 0, 0])
y = np.append(cntr[:, 0, 1], cntr[0, 0, 1])
ax.plot(x, y, c='r', linewidth=0.5, alpha=0.7)
ax.axis('off')
# Save overlay
plt.savefig('test.pdf', dpi=fig.dpi)
You can use the pgf backend instead of the default pdf backend:
plt.savefig('test.pdf', dpi=fig.dpi, backend='pgf')
This gives the correct pdf identical to the png file: link to the generated pdf file:
The reason for the mismatch is the different scaling of the image: while the difference of the red line positions between pdf and pgf backends is 2.6 µm maximum (i.e. not visually discernible), the image sizes differ by about 0.3 mm:
pdf: 115.358 x 172.861 mm with bottom left corner at 20.574 / 28.575 mm,
pgf: 115.057 x 172.606 mm with bottom left corner at 20.574 / 28.606 mm.
A dirty workaround would be to blow up the figsize. I introduced a scaling factor for this purpose at the beginning of the script. I also increased the original linewidth to 0.6 because it looked a bit nicer. The .pdf looks pretty nice
import numpy as np
import matplotlib.pyplot as plt
import numpy.matlib
import cv2
scale = 15
# Make a test image
img = np.zeros((100,100), dtype=np.uint8)
img[20:99,1:80] = 1
img = np.matlib.repmat(img, 9, 6)
# Extract contours
cntrs, hier = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# Make overlay
fig = plt.figure(figsize=(6*scale,9*scale), dpi=300//scale)
ax = fig.add_subplot()
ax.imshow(img, interpolation='none', cmap=plt.cm.gray)
for cntr in cntrs:
x = np.append(cntr[:, 0, 0], cntr[0, 0, 0])
y = np.append(cntr[:, 0, 1], cntr[0, 0, 1])
ax.plot(x, y, c='r', linewidth=0.6*scale, alpha=0.7)
ax.axis('off')
# Save overlay
plt.savefig('test.pdf')
Here the upper right corner
i'm trying to update three of the plots in my subplot. I think the problem is that the old image isn't cleared, so the new one is plotted on top of the old one..but i`m not really sure..maybe there is also another problem. But it's possible to plot a new image with cv2.imshow()...problem is that i need to plot multiple images in a subplot. How can i fix it ?
Depending on the position of the slider the mask is changing.
Thanks a lot!
img.fig2, img.ax2 = plt.subplots()
for i in range(3):
plt.subplot(2, 3, i + 1)
plt.imshow(img[i])
plt.xticks([])
plt.yticks([])
def update (val):
...........
for i in range(3):
res[i] = cv2.cvtColor(res[i], cv2.COLOR_HSV2RGB)
fig2 = plt.figure(2)
fig2.add_subplot(2, 3, 4 + i)
plt.imshow(res[i])
plt.xticks([])
plt.yticks([])
plt.draw()
plt.show()
whole code of interest:
fig2, ax2 = plt.subplots()
for i in range(3):
plt.subplot(2, 3, i + 1)
plt.imshow(img[i])
plt.xticks([])
plt.yticks([])
plt.pause(0.1)
def update(val):
hsv1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
hsv2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
hsv3 = cv2.cvtColor(img3, cv2.COLOR_BGR2HSV)
l_b = np.array([shl.val, ssl.val, svl.val])
u_b = np.array([shu.val, ssu.val, svu.val])
mask1 = cv2.inRange(hsv1, l_b, u_b)
mask2 = cv2.inRange(hsv2, l_b, u_b)
mask3 = cv2.inRange(hsv3, l_b, u_b)
res1 = cv2.bitwise_and(img1, img1, mask=mask1)
res2 = cv2.bitwise_and(img2, img2, mask=mask2)
res3 = cv2.bitwise_and(img3, img3, mask=mask3)
res = [res1, res2, res3]
plt.clf()
for i in range(3):
res[i] = cv2.cvtColor(res[i], cv2.COLOR_HSV2RGB)
fig2 = plt.figure(2)
fig2.add_subplot(2, 3, 4 + i)
plt.imshow(res[i])
plt.xticks([])
plt.yticks([])
fig2.canvas.draw_idle()
plt.pause(0.01)
plt.draw_all()
shl.on_changed(update)
ssl.on_changed(update)
svl.on_changed(update)
shu.on_changed(update)
ssu.on_changed(update)
svu.on_changed(update)
plt.show()
You're right about imshow() : it adds new images on top of the existing ones and you need to call it only once before your loop. However, it also sets the color table and you'll need to provide it with a way to know the data range. Then you can call set_data() on the AxesImage imshow returned and you will need to call flush_events() to update the screen.
Sometimes flush_events() will happen transparently because another plot activates it, but you need to make sure it happens at least once.
Here is a working solution (link)
I have written the following code that calculates the orientation of a blob using eigenvalues. When the orientation is determined, the function "straighten_up" straightens the blob out.
The only thing I'm missing to be fully satisfied, is a 1px white border in the second output figure between the black area and the green area. How can I do this?
I'm using a mask image as input:
code:
import numpy as np
import matplotlib.pyplot as plt
import cv2
img = cv2.imread('input_image.png',100)
edges = cv2.Canny(img,0,255) #searching for a border
# compute the orientation of a blob
img = edges
y, x = np.nonzero(img) # Find the index of the white pixels
x = x - np.mean(x) #The average of an array of elements
y = y - np.mean(y)
coords = np.vstack([x, y])
cov = np.cov(coords) #determine covariance matrix
evals, evecs = np.linalg.eig(cov) #eigenvectors
sort_indices = np.argsort(evals)[::-1] #Sort Eigenvalues in decreasing order
x_v1, y_v1 = evecs[:, sort_indices[0]]
x_v2, y_v2 = evecs[:, sort_indices[1]]
scale = 30
plt.plot([x_v1*-scale*2, x_v1*scale*2], #plot to show the eigenvectors
[y_v1*-scale*2, y_v1*scale*2], color='red')
plt.plot([x_v2*-scale, x_v2*scale],
[y_v2*-scale, y_v2*scale], color='blue')
plt.plot(x, y, 'k.')
plt.axis('equal')
plt.gca().invert_yaxis()
plt.show()
def straighten_up(x_v1,y_v1,coords):
theta = np.arctan((x_v1)/(y_v1))
rotation_mat =np.matrix([[np.cos(theta), -np.sin(theta)],[np.sin(theta),np.cos(theta)]])
transformed_mat = rotation_mat*coords
x_transformed, y_transformed = transformed_mat.A
fig, ax = plt.subplots(nrows=1, ncols=1)
ax = fig.add_subplot(1, 1, 1) # nrows, ncols, index
ax.set_facecolor((1.0, 0.47, 0.42))
plt.plot(x_transformed,y_transformed,"black")
straighten_up(x_v1,y_v1,coords)
plt.show()
with output:
Your x_transformed and y_transformed are the x and y coordinates of the rotated border. So you can draw them e.g. with plt.scatter. This draws dots (the third parameter is the size) on these x,y positions. Use zorder to make sure the scatter dots are not hidden by the previous parts of the plot.
Following code does just that:
fig, ax = plt.subplots(nrows=1, ncols=1)
ax = fig.add_subplot(1, 1, 1) # nrows, ncols, index
ax.set_facecolor('fuchsia')
plt.axis('equal')
plt.plot(x_transformed, y_transformed, c="lime")
plt.scatter(x_transformed, y_transformed, 1, c="white", zorder=3)
plt.show()
As you notice, there is another problem: the plot of the filled figure isn't similar to your input image. What is happening, is that plot draws lines(x[0],y[0]) to (x[1],y[1]) to (x[2],y[2]) etc.. As your x and y are only the border points, not ordered as a polygon, it is more complicated to get a correctly filled polygon. For a random input image, you can have many borders, that can form polygons with holes and islands and which can touch the image borders.
To properly get the interior points, you might get y, x = np.nonzero(img) from the original image (instead of only the edges), then do the same shift subtracting the mean of the edges, and use the same transformation matrix.
I need to return number of pixels starting by a bin value in a Histogram
def finalHisto(img):
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
hist,bins = numpy.histogram(gray.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf *hist.max()/ cdf.max()
plt.figure()
plt.title("Grayscale/CDF Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
plt.xlim([0, 256])
plt.plot(hist, color = 'b')
#posso riempire l'histogramma
#plt.hist(gray.flatten(),256,[0,256], color = 'g')
plt.plot(cdf_normalized, color = 'r')
plt.legend(('histogram','CDF'), loc = 'lower right')
plt.show()
I need to return the numbers of pixel in each bin [0,255] of all histogram
The code return this: this plot
For example I need to know the pixel number of the 55th bin
I am trying to plot an image with its respective histogram side by side with equal proportion using plt.fig() in Python but I am not getting the desired output. Instead I get the histogram overlapping onto the image.
Any idea as to why this keeps happening?
import pylab as plt
import matplotlib.image as mpimg
import numpy as np
img = np.uint8(mpimg.imread('motherT.png'))
im2 = np.uint8(mpimg.imread('waldo.png'))
# convert to grayscale
# do for individual channels R, G, B, A for nongrayscale images
img = np.uint8((0.2126* img[:,:,0]) + \
np.uint8(0.7152 * img[:,:,1]) +\
np.uint8(0.0722 * img[:,:,2]))
im2 = np.uint8((0.2126* img[:,:,0]) + \
np.uint8(0.7152 * img[:,:,1]) +\
np.uint8(0.0722 * img[:,:,2]))
# show old and new image
# show original image
fig = plt.figure()
plt.imshow(img)
plt.title(' image 1')
plt.set_cmap('gray')
# show original image
fig.add_subplot(221)
plt.title('histogram ')
plt.hist(img,10)
plt.show()
fig = plt.figure()
plt.imshow(im2)
plt.title(' image 2')
plt.set_cmap('gray')
fig.add_subplot(221)
plt.title('histogram')
plt.hist(im2,10)
plt.show()
You appear to be doing this for two images? Subplots would be your best bet. The following shows you how to use them for a 2 x 2 effect:
import pylab as plt
import matplotlib.image as mpimg
import numpy as np
img = np.uint8(mpimg.imread('motherT.png'))
im2 = np.uint8(mpimg.imread('waldo.png'))
# convert to grayscale
# do for individual channels R, G, B, A for nongrayscale images
img = np.uint8((0.2126 * img[:,:,0]) + np.uint8(0.7152 * img[:,:,1]) + np.uint8(0.0722 * img[:,:,2]))
im2 = np.uint8((0.2126 * im2[:,:,0]) + np.uint8(0.7152 * im2[:,:,1]) + np.uint8(0.0722 * im2[:,:,2]))
# show old and new image
# show original image
fig = plt.figure()
# show original image
fig.add_subplot(221)
plt.title(' image 1')
plt.set_cmap('gray')
plt.imshow(img)
fig.add_subplot(222)
plt.title('histogram ')
plt.hist(img,10)
fig.add_subplot(223)
plt.title(' image 2')
plt.set_cmap('gray')
plt.imshow(im2)
fig.add_subplot(224)
plt.title('histogram')
plt.hist(im2,10)
plt.show()
This would give you something like:
Also note, in your original code, your grey scale calculation for im2 was using the image data for img not im2.
You might want to turn the axis off for each of your images, to do this you could add plt.axis('off') before each plt.imshow() giving you: