Programmatically subtract rasters of different dimensions - python

Question:
How can I programmatically return a raster that is the difference of two (differently sized) red bands?
i.e.
gdal_calc.py -A 'WARPED.tif' -B 'DSC_1636.tif' --outfile = 'dif.tif' --calc = "A-B"
QGIS raster calculator performs this function just fine. However, the previous code returns the following error.
Exception: Error! Dimensions of file DSC_1636.tif (7380, 4928) are different from other files (7743, 5507). Cannot proceed
I am currently under the impression I should read in the rasters using a defined extent, created by finding the overlap as shown below, but I am still not able to make this work.
# Subtract two rasters of different dimensions
# Pixel coordinates define overlap
import os, sys
from PIL import Image
from osgeo import gdal, ogr, osr
gdal.UseExceptions()
# Use PIL to get information from images
im1 = Image.open('DSC_0934-warped.tif')
print('warped image size is %s ' % str(im1.size))
im2 = Image.open('DSC_1636.png')
print('initial image (image 2) size is %s' % str(im2.size))
warped image size is (7743, 5507)
initial image (image 2) size is (7380, 4928)
# Use GDAL to get information about images
def get_extent(fn):
'''Returns min_x, max_y, max_x, min_y'''
ds = gdal.Open(fn)
gt = ds.GetGeoTransform()
return (gt[0], gt[3], gt[0] + gt[1] * ds.RasterXSize,
gt[3] + gt[5] * ds.RasterYSize)
print('extent of warped.tif is %s' % str(get_extent('DSC_0934-warped.tif')))
print('extent of 1636.png is %s' % str(get_extent('DSC_1636.png')))
extent of warped.tif is (-375.3831214210602, 692.5167764068751, 7991.3588371542955, -5258.102875649754)
extent of 1636.png is (0.0, 0.0, 7380.0, 4928.0)
r1 = get_extent('DSC_0934-warped.tif')
r2 = get_extent('DSC_1636.png')
# Get left, top, right, bottom of dataset's bounds in pixel coordinates
intersection = [max(r1[0], r2[0]),
min(r1[1], r2[1]),
min(r1[2], r2[2]),
max(r1[3], r2[3])]
print('checking for overlap')
if (intersection[2] < intersection[0]) or (intersection[1] > intersection[3]):
intersection = None
print('no overlap')
else:
print('intersection overlaps at: %s' % intersection)
checking for overlap
intersection overlaps at: [0.0, 0.0, 7380.0, 4928.0]

The most straight forward answer is to read in the images as an array of defined dimensions.
Without reposting the code above used to check where the overlap is, the solution can be had with the following additions. (Thank you #Val)
# Get the data
ds1_src = gdal.Open( "DSC_1636.png" )
ds2_src = gdal.Open( "DSC_0934-warped.tif")
ds1_bnd = ds1_src.GetRasterBand(1).ReadAsArray(xoff=0, yoff=0, win_xsize=7380, win_ysize=4928)
ds2_bnd = ds2_src.GetRasterBand(1).ReadAsArray(xoff=0, yoff=0, win_xsize=7380, win_ysize=4928)
# Do the maths...
data_out = ds2_bnd - ds1_bnd
#Write the out file
driver = gdal.GetDriverByName("GTiff")
dsOut = driver.Create("out.tiff", 7380, 4928, 1, GDT_Byte)
CopyDatasetInfo(ds1_src,dsOut)
bandOut=dsOut.GetRasterBand(1)
BandWriteArray(bandOut, data_out)
#Close the datasets
ds1_src = None
ds2_src = None
ds1_bnd = None
ds2_bnd = None
bandOut = None
dsOut = None

Related

Compare cropped images python opencv

I want to compare two images to check if they are equal or not, but for that i need to compare a specific region (ROI) of both images.
I've cropped the areas i want to compare, but now i would like to know how can i do that process, because i can't directly compare the cropped images.
How can i for example get the average pixels values of both cropped images and compare them?
Update: I've solved the situation.
Current code:
import cv2
import numpy as np
from skimage.measure import compare_ssim as ssim
def mse(imageA, imageB):
# the 'Mean Squared Error' between the two images is the sum of the squared difference between the two images;
err = np.sum((imageA.astype("float") - imageB.astype("float")) ** 2)
err /= float(imageA.shape[0] * imageA.shape[1])
polarity_ok = cv2.resize(cv2.imread("polarity_OK_edited.jpg"),None,fx=0.2, fy=0.2) #resize the image to be smaller
polarity_nok = cv2.resize(cv2.imread("Polarity_NOK1.JPG"), None,fx=0.2, fy=0.2) #resize the image to be smaller
polarity_ok_cropped = polarity_ok[350:408, 97:111]
polarity_nok_cropped = polarity_nok[350:408, 97:111]
polarity_ok_cropped1 = polarity_ok[359:409, 232:240]
polarity_nok_cropped1 = polarity_nok[359:409, 232:240]
polarity_ok_cropped2 = polarity_ok[118:153, 44:69]
polarity_nok_cropped2 = polarity_nok[118:153, 44:69]
polarity_ok_cropped3 = polarity_ok[94:142, 192:197]
polarity_nok_cropped3 = polarity_nok[94:142, 192:197]
m = mse(polarity_ok_cropped, polarity_nok_cropped)
s = ssim(polarity_ok_cropped, polarity_nok_cropped, multichannel=True)
diff = cv2.subtract(polarity_ok_cropped, polarity_nok_cropped)
result = not np.any(diff)
m1 = mse(polarity_ok_cropped1, polarity_nok_cropped1)
s1 = ssim(polarity_ok_cropped1, polarity_nok_cropped1, multichannel=True)
diff1 = cv2.subtract(polarity_ok_cropped1, polarity_nok_cropped1)
result1 = not np.any(diff1)
m2 = mse(polarity_ok_cropped2, polarity_nok_cropped2)
s2 = ssim(polarity_ok_cropped2, polarity_nok_cropped2, multichannel=True)
diff2 = cv2.subtract(polarity_ok_cropped2, polarity_nok_cropped2)
result2 = not np.any(diff2)
m3 = mse(polarity_ok_cropped2, polarity_nok_cropped2)
s3 = ssim(polarity_ok_cropped2, polarity_nok_cropped2, multichannel=True)
diff3 = cv2.subtract(polarity_ok_cropped3, polarity_nok_cropped3)
result3 = not np.any(diff3)
if (result and result1 and result2 and result3):
print ("The polarity is correct. Awesome :)")
else:
print ("Nice try, but the polarity is incorrect. Take another chance!")
If you know exactly where the objects you want to compare are, simple and fast method using OpenCV to compare two images is to extract histograms using calcHistogram() for each channel (RGB or HSV) and then compare them using compareHist().
Further infos and examples might be found here: Histogram comparsion.
You can use the Structural Similarity Index (SSIM) as giving the 2 images as input and returning a score value in the range [-1, 1]. A score of 1 indicating a perfect similarity between 2 input images (In case of both images are equal)
from skimage.measure import compare_ssim
(score, diff) = compare_ssim(image1, image2, full=True)
Btw Converting the input images before comparison into grayscale is prefered.
One more way to do the same :
from PIL import Image
import math, operator
i1 = Image.open('./image1.png')
i2 = Image.open('./image2.png')
#this will resize any format of image file
assert i1.mode == i2.mode, "Different kinds of images."
assert i1.size == i2.size, "Different sizes."
pairs = zip(i1.getdata(), i2.getdata())
if len(i1.getbands()) == 1:
# for gray-scale jpegs
dif = sum(abs(p1-p2) for p1,p2 in pairs)
else:
dif = sum(abs(c1-c2) for p1,p2 in pairs for c1,c2 in zip(p1,p2))
ncomponents = i1.size[0] * i1.size[1] * 3
print ("Difference (percentage):", (dif / 255.0 * 100) / ncomponents)
You need to install pillow.
hope this will help you.

Cliping a Digital Elevation Model using GDAL

So I am working on a Arcpy toolbox in Arcgis that take DEM raster file into a certain treatment.
However I need to clip those image because the original ones are way too big and too long to process.
But thing is, Arcgis clipping tool changes the data type which I cannot use then.
I started looking for codes to do that and it appears that GDAL library might help to clip a geotiff with a shapefile. Here is the code I followed with some minor changes to adapt to my 1-channel DEM: < https://pcjericks.github.io/py-gdalogr-cookbook/raster_layers.html >
from osgeo import gdal, gdalnumeric, ogr, osr
from PIL import Image, ImageDraw
gdal.UseExceptions()
# This function will convert the rasterized clipper shapefile
# to a mask for use within GDAL.
def imageToArray(i):
"""
Converts a Python Imaging Library array to a
gdalnumeric image.
"""
a=gdalnumeric.fromstring(i.tostring(),'b')
a.shape=i.im.size[1], i.im.size[0]
return a
def arrayToImage(a):
"""
Converts a gdalnumeric array to a
Python Imaging Library Image.
"""
i=Image.fromstring('L',(a.shape[1],a.shape[0]),
(a.astype('b')).tostring())
return i
def world2Pixel(geoMatrix, x, y):
"""
Uses a gdal geomatrix (gdal.GetGeoTransform()) to calculate
the pixel location of a geospatial coordinate
"""
ulX = geoMatrix[0]
ulY = geoMatrix[3]
xDist = geoMatrix[1]
yDist = geoMatrix[5]
rtnX = geoMatrix[2]
rtnY = geoMatrix[4]
pixel = int((x - ulX) / xDist)
line = int((ulY - y) / xDist)
return (pixel, line)
#
# EDIT: this is basically an overloaded
# version of the gdal_array.OpenArray passing in xoff, yoff explicitly
# so we can pass these params off to CopyDatasetInfo
#
def OpenArray( array, prototype_ds = None, xoff=0, yoff=0 ):
ds = gdal.Open( gdalnumeric.GetArrayFilename(array) )
if ds is not None and prototype_ds is not None:
if type(prototype_ds).__name__ == 'str':
prototype_ds = gdal.Open( prototype_ds )
if prototype_ds is not None:
gdalnumeric.CopyDatasetInfo( prototype_ds, ds, xoff=xoff, yoff=yoff )
return ds
def histogram(a, bins=range(0,256)):
"""
Histogram function for multi-dimensional array.
a = array
bins = range of numbers to match
"""
fa = a.flat
n = gdalnumeric.searchsorted(gdalnumeric.sort(fa), bins)
n = gdalnumeric.concatenate([n, [len(fa)]])
hist = n[1:]-n[:-1]
return hist
def stretch(a):
"""
Performs a histogram stretch on a gdalnumeric array image.
"""
hist = histogram(a)
im = arrayToImage(a)
lut = []
for b in range(0, len(hist), 256):
# step size
step = reduce(operator.add, hist[b:b+256]) / 255
# create equalization lookup table
n = 0
for i in range(256):
lut.append(n / step)
n = n + hist[i+b]
im = im.point(lut)
return imageToArray(im)
def main( shapefile_path, raster_path ):
# Load the source data as a gdalnumeric array
srcArray = gdalnumeric.LoadFile(raster_path)
# Also load as a gdal image to get geotransform
# (world file) info
srcImage = gdal.Open(raster_path)
geoTrans = srcImage.GetGeoTransform()
# Create an OGR layer from a boundary shapefile
shapef = ogr.Open("%s.shp" % shapefile_path)
lyr = shapef.GetLayer( os.path.split( os.path.splitext( shapefile_path )[0] )[1] )
poly = lyr.GetNextFeature()
# Convert the layer extent to image pixel coordinates
minX, maxX, minY, maxY = lyr.GetExtent()
ulX, ulY = world2Pixel(geoTrans, minX, maxY)
lrX, lrY = world2Pixel(geoTrans, maxX, minY)
# Calculate the pixel size of the new image
pxWidth = int(lrX - ulX)
pxHeight = int(lrY - ulY)
clip = srcArray[ulY:lrY, ulX:lrX]
#
# EDIT: create pixel offset to pass to new image Projection info
#
xoffset = ulX
yoffset = ulY
print "Xoffset, Yoffset = ( %f, %f )" % ( xoffset, yoffset )
# Create a new geomatrix for the image
geoTrans = list(geoTrans)
geoTrans[0] = minX
geoTrans[3] = maxY
# Map points to pixels for drawing the
# boundary on a blank 8-bit,
# black and white, mask image.
points = []
pixels = []
geom = poly.GetGeometryRef()
pts = geom.GetGeometryRef(0)
for p in range(pts.GetPointCount()):
points.append((pts.GetX(p), pts.GetY(p)))
for p in points:
pixels.append(world2Pixel(geoTrans, p[0], p[1]))
rasterPoly = Image.new("L", (pxWidth, pxHeight), 1)
rasterize = ImageDraw.Draw(rasterPoly)
rasterize.polygon(pixels, 0)
mask = imageToArray(rasterPoly)
# Clip the image using the mask
clip = gdalnumeric.choose(mask, \
(clip, 0)).astype(gdalnumeric.uint8)
clip[:,:] = stretch(clip[:,:])
# Save new tiff
#
# EDIT: instead of SaveArray, let's break all the
# SaveArray steps out more explicity so
# we can overwrite the offset of the destination
# raster
#
### the old way using SaveArray
#
# gdalnumeric.SaveArray(clip, "OUTPUT.tif", format="GTiff", prototype=raster_path)
#
###
#
gtiffDriver = gdal.GetDriverByName( 'GTiff' )
if gtiffDriver is None:
raise ValueError("Can't find GeoTiff Driver")
gtiffDriver.CreateCopy( "OUTPUT.tif",
OpenArray( clip, prototype_ds=raster_path, xoff=xoffset, yoff=yoffset )
)
# Save as an 8-bit jpeg for an easy, quick preview
clip = clip.astype(gdalnumeric.uint8)
gdalnumeric.SaveArray(clip, "OUTPUT.jpg", format="JPEG")
gdal.ErrorReset()
if __name__ == '__main__':
main( "shapefile", "DEM.tiff" )
However I got a "shape mismatch ValueError":
<ipython-input-22-32e4e8197a02> in main(shapefile_path, raster_path, region_shapefile_path)
166
167 # Clip the image using the mask
--> 168 clip = gdalnumeric.choose(mask, (clip, 0)).astype(gdalnumeric.uint8)
169
/home/edgar/anaconda3/envs/gis2/lib/python2.7/site-packages/numpy/core/fromnumeric.pyc in choose(a, choices, out, mode)
399
400 """
--> 401 return _wrapfunc(a, 'choose', choices, out=out, mode=mode)
402
403
/home/edgar/anaconda3/envs/gis2/lib/python2.7/site-packages/numpy/core/fromnumeric.pyc in _wrapfunc(obj, method, *args, **kwds)
49 def _wrapfunc(obj, method, *args, **kwds):
50 try:
---> 51 return getattr(obj, method)(*args, **kwds)
52
53 # An AttributeError occurs if the object does not have
ValueError: shape mismatch: objects cannot be broadcast to a single shape
I tried to look around in the code where could it come from and I realized that this part is probably not working as it should:
minX, maxX, minY, maxY = lyr.GetExtent()
ulX, ulY = world2Pixel(geoTrans, minX, maxY)
lrX, lrY = world2Pixel(geoTrans, maxX, minY)
print("ulX, lrX, ulY, lrY : " , ulX, lrX, ulY, lrY) #image pixel coordinates of the shapefile
print(srcArray.shape) #shape of the raster image
clip = srcArray[ ulY:lrY, ulX:lrX] #extracting the shapefile zone from the raster image?
print(clip)
And returns:
('ulX, lrX, ulY, lrY : ', 35487, 37121, 3844, 5399)
(5041, 5041)
[]
Seems that those indexes are at of bounds (but strangely python doesn't bother that much) and nothing is copied.
So I tried to change a bit the code to get the "real" pixel value corresponding to the area I wish to extract by using a shapefile corresponding to my total raster image:
#shapefile corresponding to the whole raster image
region_shapef = ogr.Open("%s.shp" % region_shapefile_path)
region_lyr = region_shapef.GetLayer( os.path.split( os.path.splitext( region_shapefile_path )[0] )[1] )
RminX, RmaxX, RminY, RmaxY = region_lyr.GetExtent()
RulX, RulY = world2Pixel(geoTrans, RminX, RmaxY)
RlrX, RlrY = world2Pixel(geoTrans, RmaxX, RminY)
#linear regression to find the equivalent pixel values of the clipping zone
pX = float(srcArray.shape[1])/(RlrX - RulX)
X0 = -(RulX*pX)
pY = float(srcArray.shape[0])/(RlrY - RulY)
Y0 = -(RulY*pY)
idXi = int(ulX*pX+X0)
idXf = int(lrX*pX+X0)
idYi = int(ulY*pY+Y0)
idYf = int(lrY*pY+Y0)
clip = srcArray[idYi:idYf, idXi:idXf]
print(clip)
Returns an array that really extracted values:
[[169.4 171.3 173.7 ... 735.6 732.8 729.7]
[173.3 176.4 179.9 ... 734.3 731.5 728.7]
[177.8 182. 186.5 ... 733.1 730.3 727.5]
...
[ 73.3 77.5 83. ... 577.4 584.9 598.1]
[ 72.8 76.5 81.5 ... 583.1 593. 606.2]
[ 71.3 74.7 79. ... 588.9 599.1 612.3]]
Though I still have that goddamn:
<ipython-input-1-d7714555354e> in main(shapefile_path, raster_path, region_shapefile_path)
170
171 # Clip the image using the mask
--> 172 clip = gdalnumeric.choose(mask, (clip, 0)).astype(gdalnumeric.uint8)
173
174 # This image has 3 bands so we stretch each one to make them
/home/edgar/anaconda3/envs/gis2/lib/python2.7/site-packages/numpy/core/fromnumeric.pyc in choose(a, choices, out, mode)
399
400 """
--> 401 return _wrapfunc(a, 'choose', choices, out=out, mode=mode)
402
403
/home/edgar/anaconda3/envs/gis2/lib/python2.7/site-packages/numpy/core/fromnumeric.pyc in _wrapfunc(obj, method, *args, **kwds)
49 def _wrapfunc(obj, method, *args, **kwds):
50 try:
---> 51 return getattr(obj, method)(*args, **kwds)
52
53 # An AttributeError occurs if the object does not have
ValueError: shape mismatch: objects cannot be broadcast to a single shape
Am I missing or misunderstanding something about it? I really start to lack of ideas so if someone got an idea please that would be appreciated.
Otherwise if you know another way to clip my DEM without altering it I'm fine as well.
You can achieve that in a much more simple way using gdal.Warp() and a shapefile as a cutline
from osgeo import gdal
input_raster = "path/to/yourDEM.tif"
# or as an alternative if the input is already a gdal raster object you can use that gdal object
input_raster=gdal.Open("path/to/yourDEM.tif")
input_shape = "path/to/yourShapefile.shp" # or any other format
output_raster="path/to/outputDEM.tif" #your output raster file
ds = gdal.Warp(output_raster,
input_raster,
format = 'GTiff',
cutlineDSName = input_shape, # or any other file format
cutlineWhere="FIELD = 'whatever'" # optionally you can filter your cutline (shapefile) based on attribute values
dstNodata = -9999) # select the no data value you like
ds=None #do other stuff with ds object, it is your cropped dataset. in this case we only close the dataset.

Interpolate between two images

I'm trying to interpolate between two images in Python.
Images are of shapes (188, 188)
I wish to interpolate the image 'in-between' these two images. Say Image_1 is at location z=0 and Image_2 is at location z=2. I want the interpolated image at location z=1.
I believe this answer (MATLAB) contains a similar problem and solution.
Creating intermediate slices in a 3D MRI volume with MATLAB
I've tried to convert this code to Python as follows:
from scipy.interpolate import interpn
from scipy.interpolate import griddata
# Construct 3D volume from images
# arr.shape = (2, 182, 182)
arr = np.r_['0,3', image_1, image_2]
slices,rows,cols = arr.shape
# Construct meshgrids
[X,Y,Z] = np.meshgrid(np.arange(cols), np.arange(rows), np.arange(slices));
[X2,Y2,Z2] = np.meshgrid(np.arange(cols), np.arange(rows), np.arange(slices*2));
# Run n-dim interpolation
Vi = interpn([X,Y,Z], arr, np.array([X1,Y1,Z1]).T)
However, this produces an error:
ValueError: The points in dimension 0 must be strictly ascending
I suspect I am not constructing my meshgrid(s) properly but am kind of lost on whether or not this approach is correct.
Any ideas?
---------- Edit -----------
Found some MATLAB code that appears to solve this problem:
Interpolating Between Two Planes in 3d space
I attempted to convert this to Python:
from scipy.ndimage.morphology import distance_transform_edt
from scipy.interpolate import interpn
def ndgrid(*args,**kwargs):
"""
Same as calling ``meshgrid`` with *indexing* = ``'ij'`` (see
``meshgrid`` for documentation).
"""
kwargs['indexing'] = 'ij'
return np.meshgrid(*args,**kwargs)
def bwperim(bw, n=4):
"""
perim = bwperim(bw, n=4)
Find the perimeter of objects in binary images.
A pixel is part of an object perimeter if its value is one and there
is at least one zero-valued pixel in its neighborhood.
By default the neighborhood of a pixel is 4 nearest pixels, but
if `n` is set to 8 the 8 nearest pixels will be considered.
Parameters
----------
bw : A black-and-white image
n : Connectivity. Must be 4 or 8 (default: 8)
Returns
-------
perim : A boolean image
From Mahotas: http://nullege.com/codes/search/mahotas.bwperim
"""
if n not in (4,8):
raise ValueError('mahotas.bwperim: n must be 4 or 8')
rows,cols = bw.shape
# Translate image by one pixel in all directions
north = np.zeros((rows,cols))
south = np.zeros((rows,cols))
west = np.zeros((rows,cols))
east = np.zeros((rows,cols))
north[:-1,:] = bw[1:,:]
south[1:,:] = bw[:-1,:]
west[:,:-1] = bw[:,1:]
east[:,1:] = bw[:,:-1]
idx = (north == bw) & \
(south == bw) & \
(west == bw) & \
(east == bw)
if n == 8:
north_east = np.zeros((rows, cols))
north_west = np.zeros((rows, cols))
south_east = np.zeros((rows, cols))
south_west = np.zeros((rows, cols))
north_east[:-1, 1:] = bw[1:, :-1]
north_west[:-1, :-1] = bw[1:, 1:]
south_east[1:, 1:] = bw[:-1, :-1]
south_west[1:, :-1] = bw[:-1, 1:]
idx &= (north_east == bw) & \
(south_east == bw) & \
(south_west == bw) & \
(north_west == bw)
return ~idx * bw
def signed_bwdist(im):
'''
Find perim and return masked image (signed/reversed)
'''
im = -bwdist(bwperim(im))*np.logical_not(im) + bwdist(bwperim(im))*im
return im
def bwdist(im):
'''
Find distance map of image
'''
dist_im = distance_transform_edt(1-im)
return dist_im
def interp_shape(top, bottom, num):
if num<0 and round(num) == num:
print("Error: number of slices to be interpolated must be integer>0")
top = signed_bwdist(top)
bottom = signed_bwdist(bottom)
r, c = top.shape
t = num+2
print("Rows - Cols - Slices")
print(r, c, t)
print("")
# rejoin top, bottom into a single array of shape (2, r, c)
# MATLAB: cat(3,bottom,top)
top_and_bottom = np.r_['0,3', top, bottom]
#top_and_bottom = np.rollaxis(top_and_bottom, 0, 3)
# create ndgrids
x,y,z = np.mgrid[0:r, 0:c, 0:t-1] # existing data
x1,y1,z1 = np.mgrid[0:r, 0:c, 0:t] # including new slice
print("Shape x y z:", x.shape, y.shape, z.shape)
print("Shape x1 y1 z1:", x1.shape, y1.shape, z1.shape)
print(top_and_bottom.shape, len(x), len(y), len(z))
# Do interpolation
out = interpn((x,y,z), top_and_bottom, (x1,y1,z1))
# MATLAB: out = out(:,:,2:end-1)>=0;
array_lim = out[-1]-1
out[out[:,:,2:out] >= 0] = 1
return out
I call this as follows:
new_image = interp_shape(image_1,image_2, 1)
Im pretty sure this is 80% of the way there but I still get this error when running:
ValueError: The points in dimension 0 must be strictly ascending
Again, I am probably not constructing my meshes correctly. I believe np.mgrid should produce the same result as MATLABs ndgrid though.
Is there a better way to construct the ndgrid equivalents?
I figured this out. Or at least a method that produces desirable results.
Based on: Interpolating Between Two Planes in 3d space
def signed_bwdist(im):
'''
Find perim and return masked image (signed/reversed)
'''
im = -bwdist(bwperim(im))*np.logical_not(im) + bwdist(bwperim(im))*im
return im
def bwdist(im):
'''
Find distance map of image
'''
dist_im = distance_transform_edt(1-im)
return dist_im
def interp_shape(top, bottom, precision):
'''
Interpolate between two contours
Input: top
[X,Y] - Image of top contour (mask)
bottom
[X,Y] - Image of bottom contour (mask)
precision
float - % between the images to interpolate
Ex: num=0.5 - Interpolate the middle image between top and bottom image
Output: out
[X,Y] - Interpolated image at num (%) between top and bottom
'''
if precision>2:
print("Error: Precision must be between 0 and 1 (float)")
top = signed_bwdist(top)
bottom = signed_bwdist(bottom)
# row,cols definition
r, c = top.shape
# Reverse % indexing
precision = 1+precision
# rejoin top, bottom into a single array of shape (2, r, c)
top_and_bottom = np.stack((top, bottom))
# create ndgrids
points = (np.r_[0, 2], np.arange(r), np.arange(c))
xi = np.rollaxis(np.mgrid[:r, :c], 0, 3).reshape((r**2, 2))
xi = np.c_[np.full((r**2),precision), xi]
# Interpolate for new plane
out = interpn(points, top_and_bottom, xi)
out = out.reshape((r, c))
# Threshold distmap to values above 0
out = out > 0
return out
# Run interpolation
out = interp_shape(image_1,image_2, 0.5)
Example output:
I came across a similar problem where I needed to interpolate the shift between frames where the change did not merely constitute a translation but also changes to the shape itself . I solved this problem by :
Using center_of_mass from scipy.ndimage.measurements to calculate the center of the object we want to move in each frame
Defining a continuous parameter t where t=0 first and t=1 last frame
Interpolate the motion between two nearest frames (with regard to a specific t value) by shifting the image back/forward via shift from scipy.ndimage.interpolation and overlaying them.
Here is the code:
def inter(images,t):
#input:
# images: list of arrays/frames ordered according to motion
# t: parameter ranging from 0 to 1 corresponding to first and last frame
#returns: interpolated image
#direction of movement, assumed to be approx. linear
a=np.array(center_of_mass(images[0]))
b=np.array(center_of_mass(images[-1]))
#find index of two nearest frames
arr=np.array([center_of_mass(images[i]) for i in range(len(images))])
v=a+t*(b-a) #convert t into vector
idx1 = (np.linalg.norm((arr - v),axis=1)).argmin()
arr[idx1]=np.array([0,0]) #this is sloppy, should be changed if relevant values are near [0,0]
idx2 = (np.linalg.norm((arr - v),axis=1)).argmin()
if idx1>idx2:
b=np.array(center_of_mass(images[idx1])) #center of mass of nearest contour
a=np.array(center_of_mass(images[idx2])) #center of mass of second nearest contour
tstar=np.linalg.norm(v-a)/np.linalg.norm(b-a) #define parameter ranging from 0 to 1 for interpolation between two nearest frames
im1_shift=shift(images[idx2],(b-a)*tstar) #shift frame 1
im2_shift=shift(images[idx1],-(b-a)*(1-tstar)) #shift frame 2
return im1_shift+im2_shift #return average
if idx1<idx2:
b=np.array(center_of_mass(images[idx2]))
a=np.array(center_of_mass(images[idx1]))
tstar=np.linalg.norm(v-a)/np.linalg.norm(b-a)
im1_shift=shift(images[idx2],-(b-a)*(1-tstar))
im2_shift=shift(images[idx1],(b-a)*(tstar))
return im1_shift+im2_shift
Result example
I don't know the solution to your problem, but I don't think it's possible to do this with interpn.
I corrected the code that you tried, and used the following input images:
But the result is:
Here's the corrected code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy import interpolate
n = 8
img1 = np.zeros((n, n))
img2 = np.zeros((n, n))
img1[2:4, 2:4] = 1
img2[4:6, 4:6] = 1
plt.figure()
plt.imshow(img1, cmap=cm.Greys)
plt.figure()
plt.imshow(img2, cmap=cm.Greys)
points = (np.r_[0, 2], np.arange(n), np.arange(n))
values = np.stack((img1, img2))
xi = np.rollaxis(np.mgrid[:n, :n], 0, 3).reshape((n**2, 2))
xi = np.c_[np.ones(n**2), xi]
values_x = interpolate.interpn(points, values, xi, method='linear')
values_x = values_x.reshape((n, n))
print(values_x)
plt.figure()
plt.imshow(values_x, cmap=cm.Greys)
plt.clim((0, 1))
plt.show()
I think the main difference between your code and mine is in the specification of xi. interpn tends to be somewhat confusing to use, and I've explained it in greater detail in an older answer. If you're curious about the mechanics of how I've specified xi, see this answer of mine explaining what I've done.
This result is not entirely surprising, because interpn just linearly interpolated between the two images: so the parts which had 1 in one image and 0 in the other simply became 0.5.
Over here, since one image is the translation of the other, it's clear that we want an image that's translated "in-between". But how would interpn interpolate two general images? If you had one small circle and one big circle, is it in any way clear that there should be a circle of intermediate size "between" them? What about interpolating between a dog and a cat? Or a dog and a building?
I think you are essentially trying to "draw lines" connecting the edges of the two images and then trying to figure out the image in between. This is similar to sampling a moving video at a half-frame. You might want to check out something like optical flow, which connects adjacent frames using vectors. I'm not aware if and what python packages/implementations are available though.

Increase extent of vector layer with OGR or GDAL?

When using the OGR library or GDAL library with Python script, is it possible to increase the extent of a vector layer without actually adding new data points? In my specific case, I would like to increase the extent of vector layers associated with gpx files so that when I convert them to rasters they all have the same pixel matrix.
EDIT: An attempt of mine to use gdal.Rasterize does not produce a "tiff" file, nor does it cause an error to be reported:
import os
import gdal
import ogr
import math
os.chdir(r'C:\Users\pipi\Documents\Rogaine\Tarlo\gpx') #folder containing gpx files
vector_fn = '6_hour_Autumngaine_w_Tom_Elle.gpx' #filename of input gpxfile
pixel_size = 20 #units are in m if gpx file is left in wgs84
raster_fn = '0011a.tif' # Filename of the raster Tiff that will be created
driver = ogr.GetDriverByName('GPX')
source_ds = driver.Open(vector_fn, 0)
source_layer = source_ds.GetLayer('track_points') #returns the 'track points' layer of the data source
SR = source_layer.GetSpatialRef().ExportToWkt()
#_______USING VALUES FROM THE FILE___________
x_min1, x_max1, y_min1, y_max1 = source_layer.GetExtent()
pixel_sizey = pixel_size/(111.2*math.pow(10,3)) #determines an approximate x and y size because of geographic coordinates.
pixel_sizex = pixel_size/(math.cos(((y_max1 + y_min1)/2)*(math.pi/180))*111.2*math.pow(10,3))
print (pixel_sizey, pixel_sizex)
x_res = int((x_max1 - x_min1) / pixel_sizex)
y_res = int((y_max1 - y_min1) / pixel_sizey)
print (x_res, y_res)
layer_list = ['track_points']
gdal.Rasterize(raster_fn, vector_fn, format='GTiff', outputBounds=[x_min1, y_min1, x_max1, y_max1], outputSRS=SR, xRes=x_res, yRes=y_res, burnValues=[1], layers=layer_list)
target_ds = None
vector_fn = None
source_layer = None
source_ds = None
You need to pass options=gdal.RasterizeOptions(format='GTiff', outputBounds=[x_min1, y_min1, x_max1, y_max1], outputSRS=SR, xRes=x_res, yRes=y_res, burnValues=[1], layers=layer_list) instead of passing the individual kwargs directly. Otherwise, they will be ignored, and the command won't do what you intend. See Link and Link for details and links to the source code (often useful given the terse documentation).
I was unable to find a method to change the extent of the vector layer. However, I was able to write a python Function that uses gdal.RasterizeLayer() to produce a raster with an extent much larger than the original vector layer. The code for this function is:
import os
import gdal
import ogr
def RasterizeLarge(name, layer, extent, pixel_size):
"""Used to rasterize a layer where the raster extent is much larger than the layer extent
Arguments:
name -- (string) filename without extension of raster to be produced
layer -- (vector layer object) vector layer containing the data to be rasterized (tested with point data)
extent -- (list: x_min, x_max, y_min, y_max) extent of raster to be produced
pixel_size -- (list: x_pixel_size, y_pixel_size) 1 or 2 pixel different pixel sizes may be sent
"""
if isinstance(pixel_size, (list, tuple)):
x_pixel_size = pixel_size[0]
y_pixel_size = pixel_size[1]
else:
x_pixel_size = y_pixel_size = pixel_size
x_min, x_max, y_min, y_max = extent
# determines the x and y resolution of the file (lg = large)
x_res_lg = int((x_max - x_min) / x_pixel_size)+2
y_res_lg = int((y_max - y_min) / y_pixel_size)+2
if x_res_lg > 1 and y_res_lg > 1:
pass
else:
print ('Your pixel size is larger than the extent in one dimension or more')
return
x_min_sm, x_max_sm, y_min_sm, y_max_sm = layer.GetExtent()
if x_min_sm > x_min and x_max_sm < x_max and y_min_sm > y_min and y_max_sm < y_max:
pass
else:
print ('The extent of the layer is in one or more parts outside of the extent provided')
return
nx = int((x_min_sm - x_min)/x_pixel_size) #(number of pixels between main raster origin and minor raster)
ny = int((y_max - y_max_sm)/y_pixel_size)
x_res_sm = int((x_max_sm - x_min_sm) / x_pixel_size)+2
y_res_sm = int((y_max_sm - y_min_sm) / y_pixel_size)+2
#determines upper left corner of small layer raster
x_min_sm = x_min + nx * x_pixel_size
y_max_sm = y_max - ny * y_pixel_size
#______Creates a temporary raster file for the small raster__________
try:
# create the target raster file with 1 band
sm_ds = gdal.GetDriverByName('GTiff').Create('tempsmall.tif', x_res_sm, y_res_sm, 1, gdal.GDT_Byte)
sm_ds.SetGeoTransform((x_min_sm, x_pixel_size, 0, y_max_sm, 0, -y_pixel_size))
sm_ds.SetProjection(layer.GetSpatialRef().ExportToWkt())
gdal.RasterizeLayer(sm_ds, [1], layer, burn_values=[1])
sm_ds.FlushCache()
#______Gets data from the new raster in the form of an array________
in_band = sm_ds.GetRasterBand(1)
in_band.SetNoDataValue(0)
sm_data = in_band.ReadAsArray()
finally:
sm_ds = None #flushes data from memory. Without this you often get an empty raster.
#_____Creates an output file with the provided name and extent that contains the small raster.
name = name + '.tif'
try:
lg_ds = gdal.GetDriverByName('GTiff').Create(name, x_res_lg, y_res_lg, 1, gdal.GDT_Byte)
if lg_ds is None:
print 'Could not create tif'
return
else:
pass
lg_ds.SetProjection(layer.GetSpatialRef().ExportToWkt())
lg_ds.SetGeoTransform((x_min, x_pixel_size, 0.0, y_max, 0.0, -y_pixel_size))
lg_band = lg_ds.GetRasterBand(1)
lg_data = in_band.ReadAsArray()
lg_band.WriteArray(sm_data, xoff = nx, yoff = ny)
lg_band.SetNoDataValue(0)
lg_band.FlushCache()
lg_band.ComputeStatistics(False)
lg_band = None
finally:
del lg_ds, lg_band, in_band
os.remove('tempsmall.tif')
return

How to remove rings from convolve healpix map?

I'm applying convolution techniques to convolve 2 datasets, a healpix map with nside = 256 and a primary beam of shape (256, 256) in order to measure the total intensity from the convolved healpix map. My problem is that after convolving my map with the primary beam i get rings in my convolved map. I've tried normalizing it with either lanczos or Gaussian kernel to take care of the rings but all these approaches have failed.
In my code below, i used the query function in scipy to search for the nearest pixels in my healpix map within a given radius and take the sum of the product of the corresponding pixels in the primary beam using map coordinate. The final image i get has rings in it. Please can anyone help me solve this problem? Thanks in advance.
def query_npix(nside, npix, radius):
print 'searching for nearest pixels:......'
t1, t2 = hp.pix2ang(nside, np.arange(npix))
tree = spatial.cKDTree(zip(t1, t2))
dist, ipix_indx = tree.query(zip(t1, t2), k = 150, distance_upper_bound = radius)
r1, r2 = hp.pix2ang(nside, ipix_indx)
ra = r1.T - t1
dec = r2.T - t2
print 'Done searching'
return np.array(dist), np.array(ipix_indx), np.array(ra.T), np.array(dec.T)
def fullSky_convolve(healpix_map, primary_beam_fits, ipix_indx, dist, radius, r1, r2):
measured_map = []
hdulist = openFitsFile(primary_beam_fits)
beam_data = hdulist[0].data
header = hdulist[0].header
nside = hp.get_nside(healpix_map[0, ...])
npix = hp.get_map_size(healpix_map[0, ...]) # total number of pixels in the map must be 12 * nside^2
crpix1, crval1, cdelt1 = [ header.get(x) for x in "CRPIX1", "CRVAL1", "CDELT1" ]
crpix2, crval2, cdelt2 = [ header.get(x) for x in "CRPIX2", "CRVAL2", "CDELT2" ]
# beam centres in pixel coordinates
xc = crpix1-1 + (np.rad2deg(r1.ravel()) - crval1)/(256*cdelt1)
yc = crpix2-1 + (np.rad2deg(r2.ravel()) - crval2)/(256*cdelt2)
#xc = (np.rad2deg(r1.ravel()) )/cdelt1
for j in xrange(4):
print 'started Stokes: %d' %j
for iter in xrange(0 + j, 16, 4):
outpt = np.zeros(shape = npix, dtype=np.float64)
#by = outpt.copy()
# mask beam
bm_data = beam_data[iter]
#masked_beam= beam_data[iter]
shape = bm_data.shape
rad = np.linspace(-shape[0]/2,shape[-1]/2,shape[0])
rad2d = np.sqrt(rad[np.newaxis,:]**2+rad[:,np.newaxis]**2)
mask = rad2d <= radius/abs(cdelt2)
masked_beam = bm_data*mask
s1 = ndimage.map_coordinates(masked_beam, [xc, yc], mode = 'constant')
bm_map = s1.reshape(dist.shape[0], dist.shape[-1])
for itr in xrange(npix):
g_xy = (1.0/(np.sqrt(2*np.pi)*np.std(dist[itr])))*np.exp(-(dist[itr])**2/(2*np.var(dist[itr])))
#weighted_healpix_map = np.convolve(healpix_map[j, ...][ipix_indx[itr]], g_xy/g_xy.sum(), mode='same')
weighted_healpix_map = ndimage.filters.convolve(healpix_map[j, ...][ipix_indx[itr]], g_xy/g_xy.sum(), mode='reflect')
#outpt[itr] = np.sum(weighted_healpix_map*(bm_map[itr]/bm_map[itr].sum()))
outpt[itr] = np.sum(weighted_healpix_map*(bm_map[itr]))
#print 'itr', itr
alpha = file('pap%d.save'%iter, 'wb')
#h_map = ndimage.filters.gaussian_filter(outpt, sigma = 3.)
cPickle.dump(outpt, alpha, protocol = cPickle.HIGHEST_PROTOCOL)
alpha.close()
print 'Just dumped stripp%d.save:-------'%iter
print 'Loading dumped files:-------'
loaded_objects = []
for itr4 in xrange(16):
alpha = file('stripp%d.save'%itr4, 'rb')
loaded_objects.append(cPickle.load(alpha))
alpha.close()
measured_map.append(copy.deepcopy(loaded_objects))
return measured_map
Remember that HEALPix maps can be in either "Ring" or "Nested" format. It sounds like you may need to add the keyword nest=True to your healpy functions like hp.pix2ang. If your input maps are in nested format, this keyword is needed.
For example:
I recently tried using the healpy.smoothing() function, and found my resulting image to have rings (perhaps like you described), upon viewing the output map with healpix.mollview(). The rings disappeared and the image was presented as I expected, after running mollview with the nested=True keyword. Check what ordering schemes your input files use
Reference:
http://healpy.readthedocs.org/en/latest/tutorial.html#creating-and-manipulating-maps
Healpix supports two different ordering schemes, RING or NESTED. By
default, healpy maps are in RING ordering. In order to work with
NESTED ordering, all map related functions support the nest keyword,
for example: hp.mollview(m, nest=True, title="Mollview image NESTED")

Categories

Resources