how to add text labels to figs and merge them together - image

I have two figs fig1.png and fig2.png made of matplotlib in python code, now in some papers, I want to merge these two figs together, and give them (a) and (b) text labels in the top left area, respectively. How can I use matplotlib do it? I have the codes to generate fig1.png and fig2.png with fig1.py,fig2.py, is there any other convenient way ?like write another short code to do it?

How easily you can combine two figures depends on how complex they are. If they are both simple figures with single axes it is relatively straightforward.
Since you have the code to generate the two axes, I would start by putting that code into a single file and then modifying your code to plot to subplot axes.
You can layout multiple axes within a figure as follows:
from matplotlib.pyplot import plt
fig, ax = plt.subplots(1,2) #nrows, ncols
The variable ax is now a list of two axes onto which you can plot.
So for example, to plot to the left hand side you can use:
ax[0].plot([1,2,3,4,5]
To the right:
ax[1].plot([1,2,3,4,5]
You can also set labels on the subplots using .set_title(). A complete example:
ax[0].plot([1,2,3,4,5])
ax[0].set_title("A")
ax[1].plot([5,4,3,2,1])
ax[1].set_title("B")

Related

How to plot or show multiple images in a row using julia?

I'm new to Julia and I used MNIST handwritten digit train data to get multiple images in matrix with size 28 x 28. Let's assume I store them in array img[i] with length n(n is dynamic). I want to show Images in one window such that every image has its own specific label under it.
I tried to search and read documents, currently I use hcat(images_window, img[i]) for all images and plot(images_window) and annotate some text label for each image in specific coordinates. This way is not a good practice and n is not configurable either.
I expect Julia have something like dynamic layout for its plots and I can show Image in each subplot and show them in a window with something like this:
plt = plot()
for (i, subplot) in enumerate(plot)
plot!(plt, subplot, layout(i))
end
display(plt)
You didn't mention which plotting library you are using but from the basic syntax I'm vaguely guessing that you might be asking bout Plots.jl.
In Plots, plotting multiple subplots on one figure in principle works like this:
using Plots
p1 = plot(rand(5))
p2 = plot(rand(5))
plot(p1, p2)
i.e., you call plot with multiple arguments which themselves are plots. You can then additionally specify a layout kwarg, which in its simplest form takes a tuple of (nrows, ncols) and places the subplots in a grid with the specified number of rows and columns.
As an example, here's three plots next to each other:
plot(plot.([rand(5) for _ ∈ 1:3])..., layout = (1, 3))

Plotting circles and arc without segmentation (PLT format)

I'm having problems plotting vector objects such as arcs and circles from Autodesk without forcing them into set of lines. PLT (HPGL) file format supports both circles and arcs defined through various means, but for some reason, the output uses just lines.
The drawing I've created is really simple, just for testing purposes
and the output from plotting (I've separated commands into single lines so it's more readable)
.(;.I81;;17:.N;19:IN;
SC;
PU;
RO90;
IP;
IW;
VS20,1VS20,2VS20,3VS20,4VS20,5VS20,6VS20,7VS20,8SP1;
PU;
PA0,0;
SP1;
LT;
PA-4985,2256;
PDPA-2985,256,-2985,-1744,-4985,256,-4985,2256;
PUPA-4967,-2572;
PDPA-4862,-2569,-4757,-2563,-4653,-2552,-4549,-2538,-4446,-2520,-4343,-2498,-4241,-2473,-4141,-2443,-4041,-2410,-3943,-2373,-3846,-2333,-3751,-2289,-3657,-2241,-3566,-2190,-3476,-2136,-3388,-2078,-3303,-2017,-3219,-1953,-3139,-1886,-3060,-1816,-2985,-1744;
PUPA-1530,-26;
PDPA-1472,-24,-1414,-18,-1357,-8,-1300,5,-1245,22,-1190,42,-1137,66,-1086,94,-1037,124,-989,158,-944,195,-902,235,-862,277,-825,322,-791,369,-761,419,-733,470,-709,523,-689,577,-672,633,-659,690,-649,747,-644,805,-642,863,-644,921,-649,979,-659,1036,-672,1093,-689,1148,-709,1203,-733,1256,-761,1307,-791,1356,-825,1404,-862,1449,-902,1491,-944,1531,-989,1568,-1037,1602,-1086,1632,-1137,1660,-1190,1684,-1245,1704,-1300,1721,-1357,1734,-1414,1744,-1472,1749,-1530,1751,-1588,1749,-1646,1744,-1703,1734,-1760,1721,-1816,1704,-1870,1684,-1923,1660,-1974,1632,-2024,1602,-2071,1568,-2116,1531,-2158,1491,-2198,1449,-2235,1404,-2269,1356,-2299,1307,-2327,1256,-2351,1203,-2371,1148,-2388,1093,-2401,1036,-2411,979,-2417,921,-2418,863,-2417,805,-2411,747,-2401,690,-2388,633,-2371,577,-2351,523,-2327,470,-2299,419,-2269,369,-2235,322,-2198,277,-2158,235,-2116,195,-2071,158,-2024,124,-1974,94,-1923,66,-1870,42,-1816,22,-1760,5,-1703,-8,-1646,-18,-1588,-24,-1530,-26;
PU;
PA0,0;
SP;
Now the command for circle in PLT is really simple - CI with 3 parameters - center X, Y and radius. Instead it was substituted with bunch of lines.
I've tried various HP printers, but this seems to make no difference as the driver support is always the same, so I settled with HP 7585B. I've also tried increasing quality but this only resulted in more points.
Is there any way to get 1:1 (as by shape) vector graphics from AutoCAD to PLT? Or is there any really simple file format like PLT that would support this?
Use DXF instead of PLT. You will got your 1:1 mapping.

copyobj copies entire image instead of axes only

What I have is a plot showing the area of connected components. What I want to do is to further work on the plot figure such as clean it up a bit or imcomplement it etc. and then be able to apply the axes from the original plot to this image and be able to extract the ylabel.
Let me explain the above issue with my code and some examples.
This is the plot I have, the y-axis represents the object areas. This is the important axis that I want to transfer to the new image.
Since I am interested in the axes only I copy that using
h = findobj(gcf,'type','axes');
So that I can work with the figure without the axes and borders interfering I save it without these attributes
set(gca, 'visible', 'off'); % Hide the axis and borders
hgexport(gcf, 'plot1.jpg', hgexport('factorystyle'), 'Format', 'jpeg');
This is what I get:
So far so good.
Now comes the processing or in other words changing the plot to my needs.
plot_img = rgb2gray(imread('plot1.jpg'));
img_bw_plot = im2bw(plot_img, graythresh(plot_img));
[rows cols] = size(plot_img);
new = zeros(size(plot_img));
for i = 1: rows
for j = 1: cols
if (img_bw_plot(i,j) == 0)
new(i, 1:10) = 255;
end
end
end
f = figure;
imshow(new);
copyobj(h,f)
This produces a weird overlapped image where instead of copying only the axes, the entire image is copied on top of the new. The datacursormode also fails to work beyond the over lapping image.
First of all I'm a little bit confused that if you have the figure in the first place why aren't you extracting your data from it using something like:
lines=findobj(gca,'type','line');
y=zeros(1,length(lines));
for i=1:length(lines)
y(i)=get(lines(i),'ydata');
end
and there you'll have all the data.
But let's say the original figure isn't like a figure figure where you'd have access to the children of the axes object (though all of them being copied together kind of suggests that this is not the case). What you need to realize is that an "axes" object in MATLAB isn't just the axes of the graph, but the whole graph. For example when you have 5 subplots, each of those smaller plots is an axes object and the graph itself is one of its children which is a "line" object (refer to my example above).
So after this long lecture :), one solution is that you could manually create those axes around your newly drawn image instead of copying the axes object as such:
set(gca,'visible','on');
s=size(new);
set(gca,'ytick',linspace(1,s(1),7),'yticklabel',linspace(6000,0,7));
This should do the trick of placing 7 ticks on the y-axis in the same manner as you have on your original figure. The same method would apply to manually creating the labels for the x-axis.
(I tried putting the resulting image here but I don't have the enough reputations to do so. That's on stackoverflow bro!)
Mind you, though, that this creates the labels on the graph giving you the illusion of the same axis while the actual coordinates of the points are actually determined by the size of the image you're saving. So if you want to make sure the image is the same size, you need to work on resizing your original figure to end up being the same size, which given then 0-6000, would be a really big image.

How to plot geo-referenced image so that it "fits" the plot coordinate system in Matplotlib

A very similar question, solved the same way: how to use 'extent' in matplotlib.pyplot.imshow
I have a list of geographical coordinates (a "tracklog") that describe a geographical trajectory. Also, I have the means of obtaining an image spanning the tracklog coverage, where I know the "geographical coordinates" of the corners of the image.
My plot currently looks like this (notice the ticks - x=longitudes, y=latitudes, in UTM, WGS84):
Then suppose I know the corner coordinates of the following image (or a version of it without the blue track), and would like to plot it SO THAT IT FITS THE COORDINATE SYSTEM of the plot.
How would I do it?
(as a side note, in case that matters, I plan to use tiles)
As per the comment of Joe Kington (waiting for his actual answer so that I can accept it), the following code works as expected, giving a pannable and zoomable fixed-aspect "georeferenced" tile over which I am able to plot tracklogs:
import matplotlib.pyplot as plt
import Image
import numpy
imarray = numpy.asarray(Image.open('map.jpg'))
plt.plot([0,1], [0,1], 'o', c='red', ms=20) ## some reference circles for debugging
plt.imshow(imarray, extent=[0,1,0,1]) ## some random map whose corners have known coordinates
plt.axis('equal')
plt.show()
There is really not much of an answer here, but if you are using matplotlib, and you geos-tuff, take a look at matplotlib.basemap.
By default all operations are done on UTM maps, but you can choose your own projection.
Take also a look on the list of good tutorials in http://www.geophysique.be, for example.

Set autoscale limits on plot to have buffer around all points

I would like to plot a set of points using pyplot in matplotlib but have none of the points be on the edge of my axes. The autoscale (or something) sets the xlim and ylim such that often the first and last points lie at x = xmin or xmax making it difficult to read in some situations.
This is more often problematic with loglog() or semilog() plots because the autoscale would like xmin and xmax to be exact powers of ten, but if my data contains only three points, e.g. at xdata = [10**2,10**3,10**4] then the first and last points will lie on the border of the plot.
Attempted Workaround
This is my solution to add a 10% buffer to either side of the graph. But is there a way to do this more elegantly or automatically?
from numpy import array, log10
from matplotlib.pyplot import *
xdata = array([10**2,10**3,10**4])
ydata = xdata**2
figure()
loglog(xdata,ydata,'.')
xmin,xmax = xlim()
xbuff = 0.1*log10(xmax/xmin)
xlim(xmin*10**(-xbuff),xmax*10**(xbuff))
I am hoping for a one- or two-line solution that I can easily use whenever I make a plot like this.
Linear Plot
To make clear what I'm doing in my workaround, I should add an example in linear space (instead of log space):
plot(xdata,ydata)
xmin,xmax = xlim()
xbuff = 0.1*(xmax-xmin)
xlim(xmin-xbuff,xmax+xbuff))
which is identical to the previous example but for a linear axis.
Limits too large
A related problem is that sometimes the limits are too large. Say my data is something like ydata = xdata**0.25 so that the variance in the range is much less than a decade but ends at exactly 10**1. Then, the autoscale ylim are 10**0 to 10**1 though the data are only in the top portion of the plot. Using my workaround above, I can increase ymax so that the third point is fully within the limits but I don't know how to increase ymin so that there is less whitespace at the lower portion of my plot. i.e., the point is that I don't always want to spread my limits apart but would just like to have some constant (or proportional) buffer around all my points.
#askewchan I just succesfully achieved how to change matplotlib settings by editing matplotlibrc configuration file and running python directly from terminal. Don't know the reason yet, but matplotlibrc is not working when I run python from spyder3 (my IDE). Just follow steps here matplotlib.org/users/customizing.html.
1) Solution one (default for all plots)
Try put this in matplotlibrc and you will see the buffer increase:
axes.xmargin : 0.1 # x margin. See `axes.Axes.margins`
axes.ymargin : 0.1 # y margin See `axes.Axes.margins`
Values must be between 0 and 1.
Obs.: Due to bugs, scale is not correctly working yet. It'll be fixed for matplotlib 1.5 (mine is 1.4.3 yet...). More info:
axes.xmargin/ymargin rcParam behaves differently than pyplot.margins() #2298
Better auto-selection of axis limits #4891
2) Solution two (individually for each plot inside the code)
There is also the margins function (for put directly in the code). Example:
import numpy as np
from matplotlib import pyplot as plt
t = np.linspace(-6,6,1000)
plt.plot(t,np.sin(t))
plt.margins(x=0.1, y=0.1)
plt.savefig('plot.png')
Obs.: Here scale is working (0.1 will increase 10% of buffer before and after x-range and y-range).
A similar question was posed to the matplotlib-users list earlier this year. The most promising solution involves implementing a Locator (based on MaxNLocator in this case) to override MaxNLocator.view_limits.

Resources