Python regionprops sci-kit image - image

I am using sci-kit image to get the "regionprops" of a segmented image. I then wish to replace each of the segment labels with their corresponding statistic (e.g eccentricity).
from skimage import segmentation
from skimage.measure import regionprops
#a segmented image
labels = segmentation.slic(img1, compactness=10, n_segments=200)
propimage = labels
#props loop
for region in regionprops(labels1, properties ='eccentricity') :
eccentricity = region.eccentricity
propimage[propimage==region] = eccentricity
This runs, but the propimage values do not change from their original labels
I have also tried:
for i in range(0,max(labels)):
prop = regions[i].eccentricity #the way to cal a single prop
propimage[i]= prop
This delivers this error
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I am a recent migrant from matlab where I have implemented this, but the data structures used are completely different.
Can any one help me with this?
Thanks

Use ndimage from scipy : the sum() function can operate using your label array.
from scipy import ndimage as nd
sizes = nd.sum(label_file[0]>0, labels=label_file[0], index=np.arange(0,label_file[1])
You can then evaluate the distribution with numpy.histogram and so on.

Related

Add location marker on plotted Geopandas Dataframe using Folium

Context
I have an merged geodataframe of 1). Postalcode areas and 2). total amount of deliveries within that postalcode area in the city of Groningen called results. The geodataframe includes geometry that include Polygons and Multiploygons visualizing different Postal code areas within the city.
I am new to GeoPandas and therefore I've tried different tutorials including this one from the geopandas official website wherein I got introduced into interactive Folium maps, which I really like. I was able to plot my geodataframe using result.explore(), which resulted in the following map
The problem
So far so good, but now I want to simply place an marker using the folium libarty with the goal to calculate the distance between the marker and the postalcode areas. After some looking on the internet I found out in the quickstart guild that you need to create an folium.Map, then you need folium.Choropleth for my geodataframe and folium.Marker and add them to the folium.Map.
m = folium.Map(location=[53.21917, 6.56667], zoom_start=15)
folium.Marker(
[53.210903, 6.598276],
popup="My marker"
).add_to(m)
folium.Choropleth(results, data=results, columns="Postcode", fill_color='OrRd', name="Postalcode areas").add_to(m)
folium.LayerControl().add_to(m)
m
But when try to run the above code I get the following error:
What is the (possible) best way?
Besides my failing code (which would be great if someone could help me out). I am curious if this is the way to do it (Folium map + marker + choropleth). Is it not possible to call geodataframe.explore() which results into the map in second picture and then just add an marker on the same map? I have the feeling that I am making it too difficult, there must be an better solution using Geopandas.
you have not provided the geometry. Have found postal districts of Netherlands and used that
explore() supports will draw a point as a marker with appropriate parameters
hence two layers,
one is postal areas coloured using number of deliveries
second is point, with distance to each area calculated
import geopandas as gpd
import shapely.geometry
import pandas as pd
import numpy as np
geo_url = "https://geodata.nationaalgeoregister.nl/cbsgebiedsindelingen/wfs?request=GetFeature&service=WFS&version=2.0.0&typeName=cbs_provincie_2017_gegeneraliseerd&outputFormat=json"
gdf = gpd.read_file(geo_url).assign(
deliveries=lambda d: np.random.randint(10**4, 10**6, len(d))
)
p = gpd.GeoSeries(shapely.geometry.Point(6.598276, 53.210903), crs="epsg:4386")
# calc distances to point
gdf["distance"] = gdf.distance(p.to_crs(gdf.crs).values[0])
# dataframe of flattened distances
dfp = pd.DataFrame(
[
"<br>".join(
[f"{a} - {b:.2f}" for a, b in gdf.loc[:, ["statcode", "distance"]].values]
)
],
columns=["info"],
)
# generate colored choropleth
m = gdf.explore(
column="deliveries", categorical=True, legend=False, height=400, width=400
)
# add marker with distances
gpd.GeoDataFrame(
geometry=p,
data=dfp,
).explore(m=m, marker_type="marker")

Geoview and geopandas groupby projection error

I’m experiencing projection errors following a groupby on geodataframe. Below you will find the libraries that I am using:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import holoviews as hv
from holoviews import opts
import panel as pn
from bokeh.resources import INLINE
import geopandas as gpd
import geoviews as gv
from cartopy import crs
hv.extension('bokeh', 'matplotlib')
gv.extension('bokeh')
pd.options.plotting.backend = 'holoviews'
Whilst these are the versions of some key libraries:
bokeh 2.1.1
geopandas 0.6.1
geoviews 1.8.1
holoviews 1.13.3
I have concatenated 3 shapefiles to build a polygon picture of UK healthcare boundaries (links to files provided if needed). Unfortunately, from what i have found the UK doesn’t produce one file that combines all of those, so have had to merge the shape files from the 3 individual countries i’m interested in. The 3 shape files have a size of:
shape file 1 = (https://www.opendatani.gov.uk/dataset/department-of-health-trust-boundaries)
shape file 2 = (https://geoportal.statistics.gov.uk/datasets/5252644ec26e4bffadf9d3661eef4826_4)
shape file 3 = (https://data.gov.uk/dataset/31ab16a2-22da-40d5-b5f0-625bafd76389/local-health-boards-december-2016-ultra-generalised-clipped-boundaries-in-wales)
My code to concat them together is below:
England_CCG.drop(['objectid', 'bng_e', 'bng_n', 'long', 'lat', 'st_areasha', 'st_lengths'], inplace = True, axis = 1 )
Wales_HB.drop(['objectid', 'bng_e', 'bng_n', 'long', 'lat', 'st_areasha', 'st_lengths', 'lhb16nmw'], inplace = True, axis = 1 )
Scotland_HB.drop(['Shape_Leng', 'Shape_Area'], inplace = True, axis = 1)
#NI_HB.drop(['Shape_Leng', 'Shape_Area'], inplace = True, axis = 1 )
England_CCG.rename(columns={'ccg20cd': 'CCG_Code', 'ccg20nm': 'CCG_Name'}, inplace = True )
Wales_HB.rename(columns={'lhb16cd': 'CCG_Code', 'lhb16nm': 'CCG_Name'}, inplace = True )
Scotland_HB.rename(columns={'HBCode': 'CCG_Code', 'HBName': 'CCG_Name'}, inplace = True )
#NI_HB.rename(columns={'TrustCode': 'CCG_Code', 'TrustName': 'CCG_Name'}, inplace = True )
UK_shape = [England_CCG, Wales_HB, Scotland_HB]
Merged_Shapes = gpd.GeoDataFrame(pd.concat(UK_shape))
Each of the files has the same esri projection once joined, and the shape plots perfectly as one when I run:
Test= gv.Polygons(Merged_Shapes, vdims=[('CCG_Name')], crs=crs.OSGB())
This gives me a polygon plot of the UK, with all the area boundaries for each ccg.
To my geodataframe, I then add a new column, called ‘Country’ which attributes each CCG to whatever the country they belong to. So, all the Welsh CCGs are attributed to Wales, all the English ones to England and all the Scottish ones to Scotland. Just a simple additional grouping of the data really.
What I want to achieve is to have a dropdown next to the polygon map I am making, that will show all the CCGs in a particular country when it is selected from the drop down widget. I understand that the way to to do this is by a groupby. However, when I use the following code to achieve this:
c1 = gv.Polygons(Merged_Shapes, vdims=[('CCG_Name','Country')], crs=crs.OSGB()).groupby(['Country'])
I get a long list of projection errors stating:
“WARNING:param.project_path: While projecting a Polygons element from a PlateCarree coordinate reference system (crs) to a Mercator projection none of the projected paths were contained within the bounds specified by the projection. Ensure you have specified the correct coordinate system for your data.”
To which I am left without a map but I retain the widget. Does anyone know what is going wrong here and what a possible solution would be? its been driving me crazy!
Kind regards,
For some reason geoviews doesn't like the OSGB projection then followed by a groupby, as it tries to default back to platecaree projection.
The way I fixed it was to just make the entire dataset project in epsg:4326. For anyone who also runs into this problem, code below (it is a well documented solution:
Merged_Shapes.to_crs({'init': 'epsg:4326'},inplace=True)
gv.Polygons(Merged_Shapes, vdims=[('CCG_Name'),('Country')]).groupby('Country')
The groupby works fine after this.

Joining edited images in python using numpy image slicer

I am learning image manipulation as a beginner in python. My goal is to section my image into an nxn grid where each square is the average color (greyscale image) of the original, respectively. I succeeded in splitting the image, changing its pixel data and saving the new images. My problem is now stitching the image back together. I know the join function is pointing back to the original image, I had hoped that by saving over the tiles I could work around this.
This is my first time posting to stackoverflow (and I am super, super new to python), so apologies if I am not clear or if the formatting is wrong.
# Import packages
import numpy as np
from numpy import matlib
import PIL
import image_slicer
import math
import glob
from image_slicer import join
from PIL import Image
### Use PIL to import image
##img = Image.open("einstein.jpg")
# Display original image
# img.show()
##new_img = img.resize((256,256))
##new_img.save('einstein-256x256','png')
### new_img.show()
#Slice image into four pieces
tiles = image_slicer.slice("einstein.jpg", 16)
# Use glob to open every .png file with for loop
for filename in glob.glob("*.png"):
img=Image.open(filename)
pixels = img.load() # create the pixel map
pixelMap = img.load() #create the pixel map
#convert to array
arr = np.asarray(img)
#find mean
pixelMean = arr.mean(0).mean(0)[0]
# Convert mean to integer
IntMean = math.floor(pixelMean)
print(IntMean)
##pixel = pixelMap[0,0] #get the first pixel's value
##print(pixel)
# Loop for going through every pixel in image and converting it
for i in range(img.size[0]): # for every col:
for j in range(img.size[1]): # For every row
pixels[i,j] = (IntMean,IntMean,IntMean) # set the colour accordingly
# Save new monotone images
img.save(filename)
# Join new images into one
image = join(tiles)
# Save new image
image.save("einsteinJoined.jpg")
image.show()
Your question seems to be missing the error you get with your current code.
However, if I read it correctly, you will get back your original image, as was the problem in Split and Join images in Python. Similar to the answer accepted there, the solution is to change the image in each tile by ending your loop with:
tile.image = Image.open(filename)
Where tile is the tile corresponding to the file, you should loop over the tiles from the image_slicer.slice-function to do so. This is also given in answer to the question linked to.

Coordinates and Field values

Consider I have load a dataset as follows:
ds = yt.load('pltxxx')
The dataset includes the following fields
density, mag_vort, tracer, x_velocity, y_velocity
One can simply plot the mag_vort which is the magnitude of vorticity in 2D domain in this case, by means of:
slc = yt.SlicePlot(ds, 'z', 'mag_vort')
If I want to export the x-cooridnates, y-coordinates and vorticity_magnitude in the txt file (or numpy array) or plot it via matplotlib scatter plot
plt.scatter(x_coor, y_coor, c=mag_vort)
Is there an easy way to extract those information from dataset?
You can use a data object (in this case we use the all_data data object) to access the field values for the 'x', 'y', and 'mag_vort' fields:
ad = ds.all_data()
x = ad['x']
y = ad['y']
mag_vort = ad['mag_vort']
The arrays you get back from accessing a data object are YTArray instances. YTArray is a subclass of numpy's ndarray that has units attached.
Before you pass these arrays to matplotlib, convert them to whichever units you want to do the plot in, then cast them to numpy arrays:
x_plot = np.array(x.to('km'))
y_plot = np.array(y.to('km'))
plt.scatter(x_plot, y_plot, c=np.array(mag_vort))

Change array shape of an image in python

When I read a colour image in OpenCV, it is showing the dimensions as 256x256x3. But I need to pass it as 3x256x256 array to my neural network. How do I change the array shape, retaining the pixel locations in BGR.
You can simply transpose the array. For an example, my picture is a 10 x 10 picture:
import numpy as np
#my picture
wrong_format = np.arange(300).reshape(10,10,3)
correct_format = wrong_format.T
If it works properly, then correct_format(0,1,1) should be equal to wrong_format(1,1,0). And we can see that it is:
correct_format(0,1,1) == wrong_format(1,1,0)
True

Resources