OSMxn - How to plot to a specific position and resolution using a bounding box and figsize - geopandas

I have code using OSMNX (great tool, thank you GB!) that is attempting to convert OSMNX output to a raster file (a png image) with a specific resolution and bounding box because it needs to align with an existing raster. I am using ox.projection.project_graph() to convert to the necessary crs (UTM 33N) and ox.plot_graph() to try to plot using the required bounding box and figsize to get the desired image resolution -> raster cell size. I must be missing something because I consistently get an error (ValueError: Image size of 277704x419976 pixels is too large. It must be less than 2^16 in each direction) even though I specified a small figsize (3857 x 5833). The bounding box is in meters as is the projected_to crs.
Any help will be greatly appreciated.
Here's that section of the code:
...
# Fairly standard up to this point. G is a valid MultiDiGraph and the other variables all have appropriate values.
# This works great:
fig, ax = ox.plot_graph(G, node_color=nc, node_size=ns, node_zorder=2, edge_color=ec, edge_linewidth=ew)
# So does this:
pG = ox.projection.project_graph(G, to_crs={'init':'epsg:32633'})
fig, ax = ox.plot_graph(pG, node_color=nc, node_size=ns, node_zorder=2, edge_color=ec, edge_linewidth=ew)
# But this gives an error: (ValueError: Image size of 277704x419976 pixels is too large. It must be less than 2^16 in each direction)
# even though figsize is only 3857x5833.
fig, ax = ox.plot_graph(pG, bbox=(6686683.721299999, 6511693.721299999, 743864.5602, 628154.5602), figsize=(3857, 5833), dpi=30, node_color=nc, node_size=ns, node_zorder=2, edge_color=ec, edge_linewidth=ew)

The figsize argument is just passed along to matplotlib, where it is expected to be in inches.
import osmnx as ox
ox.config(use_cache=True, log_console=True)
G = ox.graph_from_place('Piedmont, CA, USA', network_type='drive')
Gp = ox.project_graph(G)
nodes = ox.graph_to_gdfs(Gp, edges=False)
c = nodes.unary_union.centroid
bbox = c.y + 500, c.y - 500, c.x - 500, c.x + 500
fig, ax = ox.plot_graph(Gp, bbox=bbox, figsize=(5, 5))

Thanks to GBoeing for his answer and his great work on OSMnx. After trying to come up with a standardized process for creating rasters with prescribed bounds and cell size (resolution), I came full-circle and realized that all that really needs to happen is to cancel out the dpi by dividing by 100. In other words, use figsize=(58.33, 38.57) to get a 5833x3857 image. Here's all I did:
...
# (pG is a valid projected MultiDiGraph and the other variables all have appropriate values)
# To create an image that's 5833x3857 using the 100dpi default,
# just divide the desired dimensions by 100 and use that for figsize.
fig, ax = ox.plot_graph(pG, figsize=(58.33, 38.57), bbox=(6686683.721299999, 6511693.721299999, 743864.5602, 628154.5602), dpi=100, node_color=nc, node_size=ns, node_zorder=2, edge_color=ec, edge_linewidth=ew)
fig.savefig("C:/output_path/output_file.png", dpi=100, pad_inches=0.0)
# This yields an image with the desired bounds, dimensions, and therefore cell size (resolution).

Related

Why is the whole picture turning red?

We’ve been trying to fix this program for hours, yet nothing seems to work, we just can’t figure out what the problem is. It is supposed to make the whole picture black white, besides the red pixels. (https://imgur.com/a/gxRm3N1 should look like that afterwards)
Here is the result after the using the program, the whole picture is turning red:
https://imgur.com/a/yWYVoIx
How can I fix this?
from image_helper import *
img = load_rgb_image("ara.jpg")
w, h = img.size
pixels = load_rgb_pixels("ara.jpg")
#img.show()
for i in range(w*h):
r,g,b = pixels[i]
new_r = 2*r
new_g = g // 2
new_b = b + 10
pixels[i] = (new_r, new_g, new_b)
new_img = new_rgb_image(w, h, pixels)
new_img.show()
There is an excellent solution implemented in MATLAB.
I was tempting to translate the code from MATLAB to Python (using OpenCV).
Convert the image to HSV color space.
Select "non-red" pixels. In HSV color space, the first channel is the hue - there is a range of hue for pixels that considered to be red.
Set the selected pixel saturation channel to 0. (Pixels with zero saturation are gray).
Convert the image back from HSV to BGR color space.
Here is the Python code (conversation of the original MATLAB code):
import cv2
# The following code is a Python conversion to the following MATLAB code:
# Original MATLAB code: https://stackoverflow.com/questions/4063965/how-can-i-convert-an-rgb-image-to-grayscale-but-keep-one-color
img = cv2.imread('roses.jpg') # Load image.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # Convert the image to HSV color space.
h = hsv[:, :, 0] # Note: in OpenCV hue range is [0,179]) The original MATLAB code is 360.*hsvImage(:, :, 1), when hue range is [0, 1].
s = hsv[:, :, 1] # Get the saturation plane.
non_red_idx = (h > 20//2) & (h < 340//2) # Select "non-red" pixels (divide the original MATLAB values by 2 due to the range differences).
s[non_red_idx] = 0 # Set the selected pixel saturations to 0.
hsv[:, :, 1] = s # Update the saturation plane.
new_img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) # Convert the image back to BGR space
# Show images for testing:
cv2.imshow('img', img)
cv2.imshow('new_img', new_img)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite('new_img.jpg', new_img)
Result:
Notes:
For the method used for selecting the color range, refer to the original post.
The reference post has a simpler solution, using simple for loop (with inferior results), that more resembles your code.
Consider using this code as reference.

Calculate 3D distance based on change in intensity

I have three sections (top, mid, bot) of grayscale images (3D). In each section, I have a point with coordinates (x,y) and intensity values [0-255]. The distance between each section is 20 pixels.
I created an illustration to show how those images were generated using a microscope:
Illustration
Illustration (side view): red line is the object of interest. Blue stars represents the dots which are visible in top, mid, bot section. The (x,y) coordinates of these dots are known. The length of the object remains the same but it can rotate in space - 'out of focus' (illustration shows a rotating line at time point 5). At time point 1, the red line is resting (in 2D image: 2 dots with a distance equal to the length of the object).
I want to estimate the x,y,z-coordinate of the end points (represents as stars) by using the changes in intensity, the knowledge about the length of the object and the information in the sections I have. Any help would be appreciated.
Here is an example of images:
Bot section
Mid section
Top section
My 3D PSF data:
https://drive.google.com/file/d/1qoyhWtLDD2fUy2zThYUgkYM3vMXxNh64/view?usp=sharing
Attempt so far:
enter image description here
I guess the correct approach would be to record three images with slightly different z-coordinates for your bot and your top frame, then do a 3D-deconvolution (using Richardson-Lucy or whatever algorithm).
However, a more simple approach would be as I have outlined in my comment. If you use the data for a publication, I strongly recommend to emphasize that this is just an estimation and to include the steps how you have done it.
I'd suggest the following procedure:
Since I do not have your PSF-data, I fake some by estimating the PSF as a 3D-Gaussiamn. Of course, this is a strong simplification, but you should be able to get the idea behind it.
First, fit a Gaussian to the PSF along z:
[xg, yg, zg] = meshgrid(-32:32, -32:32, -32:32);
rg = sqrt(xg.^2+yg.^2);
psf = exp(-(rg/8).^2) .* exp(-(zg/16).^2);
% add some noise to make it a bit more realistic
psf = psf + randn(size(psf)) * 0.05;
% view psf:
%
subplot(1,3,1);
s = slice(xg,yg,zg, psf, 0,0,[]);
title('faked PSF');
for i=1:2
s(i).EdgeColor = 'none';
end
% data along z through PSF's center
z = reshape(psf(33,33,:),[65,1]);
subplot(1,3,2);
plot(-32:32, z);
title('PSF along z');
% Fit the data
% Generate a function for a gaussian distibution plus some background
gauss_d = #(x0, sigma, bg, x)exp(-1*((x-x0)/(sigma)).^2)+bg;
ft = fit ((-32:32)', z, gauss_d, ...
'Start', [0 16 0] ... % You may find proper start points by looking at your data
);
subplot(1,3,3);
plot(-32:32, z, '.');
hold on;
plot(-32:.1:32, feval(ft, -32:.1:32), 'r-');
title('fit to z-profile');
The function that relates the intensity I to the z-coordinate is
gauss_d = #(x0, sigma, bg, x)exp(-1*((x-x0)/(sigma)).^2)+bg;
You can re-arrange this formula for x. Due to the square root, there are two possibilities:
% now make a function that returns the z-coordinate from the intensity
% value:
zfromI = #(I)ft.sigma * sqrt(-1*log(I-ft.bg))+ft.x0;
zfromI2= #(I)ft.sigma * -sqrt(-1*log(I-ft.bg))+ft.x0;
Note that the PSF I have faked is normalized to have one as its maximum value. If your PSF data is not normalized, you can divide the data by its maximum.
Now, you can use zfromI or zfromI2 to get the z-coordinate for your intensity. Again, I should be normalized, that is the fraction of the intensity to the intensity of your reference spot:
zfromI(.7)
ans =
9.5469
>> zfromI2(.7)
ans =
-9.4644
Note that due to the random noise I have added, your results might look slightly different.

Scale image object to match another object's scale

I have two sets of images of different size for each set. The first set is images of 400x400 pixels with real picture objects.
The second set is 319x319, with image silhouettes of different scale than the real picture objects.
What I want to achieve, is basically to have the silhouettes replaced by the real picture objects (i.e. beaver) of the first set. So the end result will be 319x319 resolution images with real picture objects. Here is an example:
The first set images cannot simply be resized to 319x319, since the beaver will not match the silhouette. There are about 100 images with different "beaver size to beaver's silhouette size" relationships. Is there a way to automate this procedure?
So far, I've tried #cxw suggestion up to step 2. Here is the code of EllipseDirectFit I used. And here is my code to plot the images with the ellipse fits. I don't know how to proceed to steps 3-5.. I think from EllipseDirectFit function -> 2*abs(A(1)) should be the ellipsi's major axes. (NOTE: 'a1.bmp' is the real image and 'b1.bmp' is the silhouette).
In case anyone else has the same problem as me, I post the code that solved my problem. I actually followed cxw's suggestion and fitted an ellipse for both real and silhouette pictures, then resized the real picture based on the ratio of the silhouette-ellipse's major axis to the real-ellipse major axis. This made the image object match in size the silhouette image object (i.e. the beaver). Then I either cropped, or added border pixels to match the resolution I needed (i.e. 319x319).
% fetching the images
realList = getAllFiles('./real_images'); % getAllFiles => StackOverflow function
silhList = getAllFiles('./silhouettes');
for qq = 1:numel(realList)
% Name of the file to save
str = realList{qq}(15:end);
a = imread(realList{qq}); % assign real image
background_Ra = a(1,1,1); % getting the background colors
background_Ga = a(1,1,2);
background_Ba = a(1,1,3);
% finding the points (x,y) to pass to fit_ellipse
[x1,y1]=find(a(:,:,1)~=background_Ra | a(:,:,2)~=background_Ga | a(:,:,3)~=background_Ba);
% fitting an ellipse to these points
z1 = fit_ellipse(x1,y1); % Mathworks file exchange function
b = imread(silhList{qq}); % assign silhouette image
background_R2b = b(1,1,1); % getting the background colors
background_G2b = b(1,1,2);
background_B2b = b(1,1,3);
% finding the points (x,y) to pass to fit_ellipse
[x2,y2]=find(b(:,:,1)~=background_R2b & b(:,:,2)~=background_G2b & b(:,:,3)~=background_B2b);
% fitting an ellipse to these points
z2 = fit_ellipse(x2,y2);
% ratio of silhouette's ellipse major axis to real image's ellipse
% major axis
ellaxratio = z2.long_axis/z1.long_axis;
% resizing based on ellaxratio, so that the real image object size will
% now fit the silhouette's image object size
c = imresize(a,ellaxratio); c = rgb2gray(c);
bordercolor = c(end,end);
% if the resulting image is smaller, add pixels around it until they
% match with the silhouette image resolution
if size(c) < 319
while size(c) < 319
% 'addborder' is a Mathworks file exchange function
c = addborder(c(:,:,1),1, bordercolor ,'outer');
end
% if the resulting image is larger, crop pixels until they match
else size(c) > 319
while size(c) > 319
c = c(2:end-1,2:end-1);
end
end
% in a few cases, the resulting resolution is 318x318, instead of
% 319x319, so a small adjustment won't hurt.
if size(c) ~= 319
c = imresize(c,[319 319]);
end
% saving..
imwrite(c,['./good_fits/' str '.bmp'])
end
I don't have code for this, but here's how I would proceed, just off-hand. There's almost certainly a better way :) .
For each of the real image and the silhouette image:
Get the X, Y coordinates of the pixels that aren't the background. Edit Example tested in Octave:
background_R = img(1,1,1)
background_G = img(1,1,2)
background_B = img(1,1,3)
[xs,ys]=find(img(:,:,1)~=background_R | img(:,:,2)~=background_G | img(:,:,3)~=background_B)
The logical OR is because the image can differ from the background in any color component.
Fit an ellipse to the X, Y coordinate pairs you found. E.g., use this routine from File Exchange. (Actually, I suppose you could use a circle fit or any other shape fit you wanted, as long as size and position are the only differences between the non-background portions of the images.)
Now you have ellipse parameters for the real image and the silhouette image. Assuming the aspect ratios are the same, those ellipses should differ only in center and scale.
Resize the real image (imresize) based on the ratio of silhouette ellipse major axis length to real image ellipse major axis length. Now they should be the same size.
Find the centers. Using the above fit routine,
A=EllipseDirectFit(...)
% switch to Mathworld notation from http://mathworld.wolfram.com/Ellipse.html
ma=A(1); mb=A(2)/2; mc=A(3); md=A(4)/2; mf=A(5)/2; mg=A(6);
center_x = (mc*md-mb*mf)/(mb**2-ma*mc)
center_y = (ma*mf-mb*md)/(mb**2-ma*mc)
Move the real image data in a 3-d matrix so that the ellipse centers
coincide. For example,
cx_silhouette = ... (as above, for the silhouette image)
cy_silhouette = ...
cx_real = ... (as above, for the *resized* real image)
cy_real = ...
shifted = zeros(size(silhouette_image)) % where we're going to put the real image
deltax = cx_silhouette - cx_real
deltay = cy_silhouette - cy_real
% if deltax==deltay==0, you're done with this step. If not:
portion = resized_real_image(max(deltay,0):319-abs(deltay), max(deltax,0):319-abs(deltax), :); % or something like that - grab the overlapping part of the resized real image
shifted(max(deltay,0):min(deltay+319,319), max(deltax,0):min(deltax+319,319), :) = portion; % or something like that - slide the portion of the resized real image in x and y. Now _shifted_ should line up with the silhouette image.
Using the background color (or the black silhouette — same difference) as a mask, copy pixels from the resized, moved real image into the silhouette image.
Hope this helps!

Image Fusion using stationary wavelet transform (SWT) (MATLAB)

I'm trying to fuse two images using SWT. But I'm getting this error :
The level of decomposition 1
and the size of the image (1,5)
are not compatible.
Suggested size: (2,6)
How to change the size of the image to make it compatible for transform?
Code I've used is :
clc
i=1;
fol=1;
n=0;
for fol=1:5
f='folder';
folder = strcat(f, num2str(fol));
cd(folder)
d= numel(D);
i=(n+1);
Fname1 = strcat(int2str(i),'.bmp');
Fname2 = strcat(int2str(i+1),'.bmp');
im1 = imread(Fname1);
im2 = imread(Fname2);
im1=double(im1);
im2=double(im2);
% image decomposition using discrete stationary wavelet transform
[A1L1,H1L1,V1L1,D1L1] = swt2(im1,1,'sym2');
[A2L1,H2L1,V2L1,D2L1] = swt2(im2,1,'sym2');
% fusion start
AfL1 = 0.5*(A1L1+A2L1);
D = (abs(H1L1)-abs(H2L1))>=0;
HfL1 = D.*H1L1 + (~D).*H2L1;
D = (abs(V1L1)-abs(V2L1))>=0;
VfL1 = D.*V1L1 + (~D).*V2L1;
D = (abs(D1L1)-abs(D2L1))>=0;
DfL1 = D.*D1L1 + (~D).*D2L1;
% fused image
imf = iswt2(AfL1,HfL1,VfL1,DfL1,'sym2');
figure;
imshow(imf,[]);
Iname= strcat(int2str(fol),'.bmp');
imwrite(imf,Iname);
end
To address your first problem, that image is really small. I'm assuming that's an image of size 1 x 5. I would suggest changing your image so that it's larger, or perhaps do an imresize on the image. However, as what Ander said in his comment to you... I wouldn't call a 1 x 5 matrix an image.
To address your second problem, once you finally load in an image, the wavelet transform will most likely give you floating point numbers that are beyond the dynamic range of any sensible floating point precision image. As such, it's good that you normalize the image first, then save it to file.
Therefore, do this right before you save the image:
%// ...
%// Your code...
imshow(imf,[]);
%// Normalize the image - Change
imf = (imf - min(imf(:))) / (max(imf(:)) - min(imf(:)));
%// Your code again
%// Now save
Iname= strcat(int2str(fol),'.bmp');
imwrite(imf,Iname);
The above transformation normalizes an image so that the minimum is 0 and the maximum is 1. Once you do that, it should be visualized properly. FWIW, doing imshow(imf,[]); does this normalization for you and displays that result, but it doesn't modify the image.

Extracting numerical data from an intensity printout

I'm looking to create an intensity matrix in Matlab from an intensity image plot (saved as a jpeg (RGB) file) that was made using what appears to be the jet colormap from Matlab. I am essentially trying to reverse engineer the numerical data from the plot. The original image along with the color bar is linked (I do not have enough reputation to insert images).
http://i.imgur.com/BmryO6W.png
I initially thought this could be done with the rgb2gray command, but it produces the following image with the jet colormap applied which does not match the original image.
http://i.imgur.com/RlBei2z.png
As far as I can tell, the only route available from here is to try matching the RGB value for each pixel to a value in the colormap lookup table. Any suggestions on if this the fastest approach?
It looks like your method using rgb2gray is almost working, apart from the scale. Because the colormap is scaled automatically to the contents of your plot, I think you will have to re-scale it manually (unless you can automatically detect the tick labels on the colorbar). You can do this using the following formula:
% Some random data like yours
x = rand(1000) * 256;
% Scale data to fit your range
xRange = [min(x(:)) max(x(:))];
scaleRange = [-10 10];
y = (x - xRange(1)) * diff(scaleRange) / diff(xRange) + scaleRange(1);
You can check the operation's success with
>> [min(y(:)) max(y(:))]
ans =
-10 10

Resources