diff --git a/CMakeLists.txt b/CMakeLists.txt index cb19355..0fad3f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ find_package(FFTW REQUIRED) find_package(Motif) find_package(OpenMP REQUIRED COMPONENTS C CXX Fortran) find_package(OpenCV COMPONENTS core highgui imgproc) +find_package(pybind11 CONFIG) # Find these, and create IMPORTED INTERFACE libraries for them if they exist include(TargetGDAL) diff --git a/contrib/PyCuAmpcor/CMakeLists.txt b/contrib/PyCuAmpcor/CMakeLists.txt index cd5acfd..c9663fb 100644 --- a/contrib/PyCuAmpcor/CMakeLists.txt +++ b/contrib/PyCuAmpcor/CMakeLists.txt @@ -1,7 +1,7 @@ # Early exit if prereqs not available if(NOT TARGET GDAL::GDAL -OR NOT TARGET Python::NumPy OR NOT TARGET CUDA::cufft +OR NOT pybind11_FOUND ) return() endif() @@ -9,8 +9,8 @@ endif() set(CMAKE_CUDA_STANDARD 11) set(CMAKE_CUDA_STANDARD_REQUIRED TRUE) -cython_add_module(PyCuAmpcor - src/PyCuAmpcor.pyx +pybind11_add_module(PyCuAmpcor + src/PyCuAmpcor.cpp src/GDALImage.cu src/SConscript src/cuAmpcorChunk.cu @@ -34,7 +34,6 @@ target_include_directories(PyCuAmpcor PRIVATE target_link_libraries(PyCuAmpcor PRIVATE CUDA::cufft GDAL::GDAL - Python::NumPy ) InstallSameDir( diff --git a/contrib/PyCuAmpcor/src/PyCuAmpcor.cpp b/contrib/PyCuAmpcor/src/PyCuAmpcor.cpp new file mode 100644 index 0000000..9922085 --- /dev/null +++ b/contrib/PyCuAmpcor/src/PyCuAmpcor.cpp @@ -0,0 +1,103 @@ +#include +#include + +#include "cuAmpcorController.h" +#include "cuAmpcorParameter.h" + +PYBIND11_MODULE(PyCuAmpcor, m) +{ + m.doc() = "Python module controller for underlying CUDA-Ampcor code"; + + using str = std::string; + using cls = cuAmpcorController; + + pybind11::class_(m, "PyCuAmpcor") + .def(pybind11::init<>()) + + // define a trivial binding for a controller method +#define DEF_METHOD(name) def(#name, &cls::name) + + // define a trivial getter/setter for a controller parameter +#define DEF_PARAM_RENAME(T, pyname, cppname) \ + def_property(#pyname, [](const cls& self) -> T { \ + return self.param->cppname; \ + }, [](cls& self, const T i) { \ + self.param->cppname = i; \ + }) + + // same as above, for even more trivial cases where pyname == cppname +#define DEF_PARAM(T, name) DEF_PARAM_RENAME(T, name, name) + + .DEF_PARAM(int, algorithm) + .DEF_PARAM(int, deviceID) + .DEF_PARAM(int, nStreams) + .DEF_PARAM(int, derampMethod) + + .DEF_PARAM(str, referenceImageName) + .DEF_PARAM(int, referenceImageHeight) + .DEF_PARAM(int, referenceImageWidth) + .DEF_PARAM(str, secondaryImageName) + .DEF_PARAM(int, secondaryImageHeight) + .DEF_PARAM(int, secondaryImageWidth) + + .DEF_PARAM(int, numberWindowDown) + .DEF_PARAM(int, numberWindowAcross) + + .DEF_PARAM_RENAME(int, windowSizeHeight, windowSizeHeightRaw) + .DEF_PARAM_RENAME(int, windowSizeWidth, windowSizeWidthRaw) + + .DEF_PARAM(str, offsetImageName) + .DEF_PARAM(str, grossOffsetImageName) + .DEF_PARAM(int, mergeGrossOffset) + .DEF_PARAM(str, snrImageName) + .DEF_PARAM(str, covImageName) + + .DEF_PARAM(int, rawDataOversamplingFactor) + .DEF_PARAM(int, corrStatWindowSize) + + .DEF_PARAM(int, numberWindowDownInChunk) + .DEF_PARAM(int, numberWindowAcrossInChunk) + + .DEF_PARAM(int, useMmap) + + .DEF_PARAM_RENAME(int, halfSearchRangeAcross, halfSearchRangeAcrossRaw) + .DEF_PARAM_RENAME(int, halfSearchRangeDown, halfSearchRangeDownRaw) + + .DEF_PARAM_RENAME(int, referenceStartPixelAcrossStatic, referenceStartPixelAcross0) + .DEF_PARAM_RENAME(int, referenceStartPixelDownStatic, referenceStartPixelDown0) + + .DEF_PARAM_RENAME(int, corrSurfaceOverSamplingMethod, oversamplingMethod) + .DEF_PARAM_RENAME(int, corrSurfaceOverSamplingFactor, oversamplingFactor) + + .DEF_PARAM_RENAME(int, mmapSize, mmapSizeInGB) + + .DEF_PARAM_RENAME(int, skipSampleDown, skipSampleDownRaw) + .DEF_PARAM_RENAME(int, skipSampleAcross, skipSampleAcrossRaw) + .DEF_PARAM_RENAME(int, corrSurfaceZoomInWindow, zoomWindowSize) + + .DEF_METHOD(runAmpcor) + + .def("checkPixelInImageRange", [](const cls& self) { + self.param->checkPixelInImageRange(); + }) + + .def("setupParams", [](cls& self) { + self.param->setupParameters(); + }) + + .def("setConstantGrossOffset", [](cls& self, const int goDown, + const int goAcross) { + self.param->setStartPixels( + self.param->referenceStartPixelDown0, + self.param->referenceStartPixelAcross0, + goDown, goAcross); + }) + .def("setVaryingGrossOffset", [](cls& self, std::vector vD, + std::vector vA) { + self.param->setStartPixels( + self.param->referenceStartPixelDown0, + self.param->referenceStartPixelAcross0, + vD.data(), vA.data()); + }) + ; +} diff --git a/contrib/PyCuAmpcor/src/PyCuAmpcor.pyx b/contrib/PyCuAmpcor/src/PyCuAmpcor.pyx deleted file mode 100644 index 8af89d2..0000000 --- a/contrib/PyCuAmpcor/src/PyCuAmpcor.pyx +++ /dev/null @@ -1,456 +0,0 @@ -# -# PYX file to control Python module interface to underlying CUDA-Ampcor code -# - -from libcpp.string cimport string -import numpy as np -cimport numpy as np - - -cdef extern from "cudaUtil.h": - int gpuDeviceInit(int) - void gpuDeviceList() - -def listGPU(): - gpuDeviceList() - -def setGPU(int id): - return gpuDeviceInit(id) - -def version(): - return "2.0.0" - -cdef extern from "cuAmpcorParameter.h": - cdef cppclass cuAmpcorParameter: - cuAmpcorParameter() except + - int algorithm ## Cross-correlation algorithm: 0=freq domain 1=time domain - int deviceID ## Targeted GPU device ID - int nStreams ## Number of streams to asynchonize data transfers and compute kernels - int derampMethod ## Method for deramping 0=None, 1=average, 2=phase gradient - - ## chip or window size for raw data - int windowSizeHeightRaw ## Template window height (original size) - int windowSizeWidthRaw ## Template window width (original size) - int searchWindowSizeHeightRaw ## Search window height (original size) - int searchWindowSizeWidthRaw ## Search window width (orignal size) - int halfSearchRangeDownRaw ##(searchWindowSizeHeightRaw-windowSizeHeightRaw)/2 - int halfSearchRangeAcrossRaw ##(searchWindowSizeWidthRaw-windowSizeWidthRaw)/2 - ## chip or window size after oversampling - int rawDataOversamplingFactor ## Raw data overampling factor (from original size to oversampled size) - - ## strides between chips/windows - int skipSampleDownRaw ## Skip size between neighboring windows in Down direction (original size) - int skipSampleAcrossRaw ## Skip size between neighboring windows in across direction (original size) - - int corrStatWindowSize ## Size of the raw correlation surface extracted for statistics - - ## Zoom in region near location of max correlation - int zoomWindowSize ## Zoom-in window size in correlation surface (same for down and across directions) - int oversamplingFactor ## Oversampling factor for interpolating correlation surface - int oversamplingMethod ## Correlation surface oversampling method 0=fft, 1=sinc - - float thresholdSNR ## Threshold of Signal noise ratio to remove noisy data - - ##reference image - string referenceImageName ## reference SLC image name - int imageDataType1 ## reference image data type, 2=cfloat=complex=float2 1=float - int referenceImageHeight ## reference image height - int referenceImageWidth ## reference image width - - ##secondary image - string secondaryImageName ## secondary SLC image name - int imageDataType2 ## secondary image data type, 2=cfloat=complex=float2 1=float - int secondaryImageHeight ## secondary image height - int secondaryImageWidth ## secondary image width - - int useMmap ## whether to use mmap - int mmapSizeInGB ## mmap buffer size in unit of Gigabytes (if not mmmap, the buffer size) - - ## total number of chips/windows - int numberWindowDown ## number of total windows (down) - int numberWindowAcross ## number of total windows (across) - int numberWindows ## numberWindowDown*numberWindowAcross - - ## number of chips/windows in a batch/chunk - int numberWindowDownInChunk ## number of windows processed in a chunk (down) - int numberWindowAcrossInChunk ## number of windows processed in a chunk (across) - int numberWindowsInChunk ## numberWindowDownInChunk*numberWindowAcrossInChunk - int numberChunkDown ## number of chunks (down) - int numberChunkAcross ## number of chunks (across) - int numberChunks - - int *referenceStartPixelDown ## reference starting pixels for each window (down) - int *referenceStartPixelAcross ## reference starting pixels for each window (across) - int *secondaryStartPixelDown ## secondary starting pixels for each window (down) - int *secondaryStartPixelAcross ## secondary starting pixels for each window (across) - int *grossOffsetDown ## Gross offsets between reference and secondary windows (down) : secondaryStartPixel - referenceStartPixel - int *grossOffsetAcross ## Gross offsets between reference and secondary windows (across) - int grossOffsetDown0 ## constant gross offset (down) - int grossOffsetAcross0 ## constant gross offset (across) - int mergeGrossOffset ## whether to merge gross offset to final offset - - int referenceStartPixelDown0 ## the first pixel of reference image (down), be adjusted with margins and gross offset - int referenceStartPixelAcross0 ## the first pixel of reference image (across) - int *referenceChunkStartPixelDown ## array of starting pixels for all reference chunks (down) - int *referenceChunkStartPixelAcross ## array of starting pixels for all reference chunks (across) - int *secondaryChunkStartPixelDown ## array of starting pixels for all secondary chunks (down) - int *secondaryChunkStartPixelAcross ## array of starting pixels for all secondary chunks (across) - int *referenceChunkHeight ## array of heights of all reference chunks, required when loading chunk to GPU - int *referenceChunkWidth ## array of width of all reference chunks - int *secondaryChunkHeight ## array of width of all reference chunks - int *secondaryChunkWidth ## array of width of all secondary chunks - int maxReferenceChunkHeight ## max height for all reference chunks, determine the size of reading cache in GPU - int maxReferenceChunkWidth ## max width for all reference chunks, determine the size of reading cache in GPU - int maxSecondaryChunkHeight ## max height for secondary chunk - int maxSecondaryChunkWidth ## max width for secondary chunk - - string grossOffsetImageName ## Output Gross Offset fields filename - string offsetImageName ## Output Offset fields filename - string snrImageName ## Output SNR filename - string covImageName ## Output COV filename - - ## set start pixels for reference/secondary windows - void setStartPixels(int*, int*, int*, int*) ## varying locations for reference and secondary - void setStartPixels(int, int, int*, int*) ## first window location for reference, varying for secondary - void setStartPixels(int, int, int, int) ## first window locations for reference and secondary - - void checkPixelInImageRange() ## check whether all windows are within image range - void setupParameters() ## Process other parameters after Python Inpu - -cdef extern from "cuAmpcorController.h": - cdef cppclass cuAmpcorController: - cuAmpcorController() except + - cuAmpcorParameter *param - void runAmpcor() - -cdef class PyCuAmpcor(object): - ''' - Python interface for cuda Ampcor - ''' - cdef cuAmpcorController c_cuAmpcor - def __cinit__(self): - return - - @property - def algorithm(self): - return self.c_cuAmpcor.param.algorithm - @algorithm.setter - def algorithm(self, int a): - self.c_cuAmpcor.param.algorithm = a - @property - def deviceID(self): - return self.c_cuAmpcor.param.deviceID - @deviceID.setter - def deviceID(self, int a): - self.c_cuAmpcor.param.deviceID = a - @property - def nStreams(self): - return self.c_cuAmpcor.param.nStreams - @nStreams.setter - def nStreams(self, int a): - self.c_cuAmpcor.param.nStreams = a - @property - def useMmap(self): - return self.c_cuAmpcor.param.useMmap - @useMmap.setter - def useMmap(self, int a): - self.c_cuAmpcor.param.useMmap = a - @property - def mmapSize(self): - return self.c_cuAmpcor.param.mmapSizeInGB - @mmapSize.setter - def mmapSize(self, int a): - self.c_cuAmpcor.param.mmapSizeInGB = a - @property - def derampMethod(self): - return self.c_cuAmpcor.param.derampMethod - @derampMethod.setter - def derampMethod(self, int a): - self.c_cuAmpcor.param.derampMethod = a - @property - def windowSizeHeight(self): - return self.c_cuAmpcor.param.windowSizeHeightRaw - @windowSizeHeight.setter - def windowSizeHeight(self, int a): - self.c_cuAmpcor.param.windowSizeHeightRaw = a - @property - def windowSizeWidth(self): - return self.c_cuAmpcor.param.windowSizeWidthRaw - @windowSizeWidth.setter - def windowSizeWidth(self, int a): - self.c_cuAmpcor.param.windowSizeWidthRaw = a - @property - def halfSearchRangeDown(self): - """half of the search range""" - return self.c_cuAmpcor.param.halfSearchRangeDownRaw - @halfSearchRangeDown.setter - def halfSearchRangeDown(self, int a): - """set half of the search range""" - self.c_cuAmpcor.param.halfSearchRangeDownRaw = a - @property - def halfSearchRangeAcross(self): - """half of the search range""" - return self.c_cuAmpcor.param.halfSearchRangeAcrossRaw - @halfSearchRangeAcross.setter - def halfSearchRangeAcross(self, int a): - """set half of the search range""" - self.c_cuAmpcor.param.halfSearchRangeAcrossRaw = a - @property - def searchWindowSizeHeight(self): - return self.c_cuAmpcor.param.searchWindowSizeHeightRaw - @property - def searchWindowSizeWidth(self): - return self.c_cuAmpcor.param.searchWindowSizeWidthRaw - @property - def skipSampleDown(self): - return self.c_cuAmpcor.param.skipSampleDownRaw - @skipSampleDown.setter - def skipSampleDown(self, int a): - self.c_cuAmpcor.param.skipSampleDownRaw = a - @property - def skipSampleAcross(self): - return self.c_cuAmpcor.param.skipSampleAcrossRaw - @skipSampleAcross.setter - def skipSampleAcross(self, int a): - self.c_cuAmpcor.param.skipSampleAcrossRaw = a - - @property - def rawDataOversamplingFactor(self): - """anti-aliasing oversampling factor""" - return self.c_cuAmpcor.param.rawDataOversamplingFactor - @rawDataOversamplingFactor.setter - def rawDataOversamplingFactor(self, int a): - self.c_cuAmpcor.param.rawDataOversamplingFactor = a - @property - def corrStatWindowSize(self): - """Size of correlation surface extracted for statistics""" - return self.c_cuAmpcor.param.corrStatWindowSize - @corrStatWindowSize.setter - def corrStatWindowSize(self, int a): - self.c_cuAmpcor.param.corrStatWindowSize = a - @property - def corrSurfaceZoomInWindow(self): - """Zoom-In Window Size for correlation surface""" - return self.c_cuAmpcor.param.zoomWindowSize - @corrSurfaceZoomInWindow.setter - def corrSurfaceZoomInWindow(self, int a): - self.c_cuAmpcor.param.zoomWindowSize = a - @property - def corrSurfaceOverSamplingFactor(self): - """Oversampling factor for correlation surface""" - return self.c_cuAmpcor.param.oversamplingFactor - @corrSurfaceOverSamplingFactor.setter - def corrSurfaceOverSamplingFactor(self, int a): - self.c_cuAmpcor.param.oversamplingFactor = a - @property - def corrSurfaceOverSamplingMethod(self): - """Oversampling method for correlation surface(0=fft,1=sinc)""" - return self.c_cuAmpcor.param.oversamplingMethod - @corrSurfaceOverSamplingMethod.setter - def corrSurfaceOverSamplingMethod(self, int a): - self.c_cuAmpcor.param.oversamplingMethod = a - @property - def referenceImageName(self): - return self.c_cuAmpcor.param.referenceImageName - @referenceImageName.setter - def referenceImageName(self, str a): - self.c_cuAmpcor.param.referenceImageName = a.encode() - @property - def secondaryImageName(self): - return self.c_cuAmpcor.param.secondaryImageName - @secondaryImageName.setter - def secondaryImageName(self, str a): - self.c_cuAmpcor.param.secondaryImageName = a.encode() - - @property - def referenceImageHeight(self): - return self.c_cuAmpcor.param.referenceImageHeight - @referenceImageHeight.setter - def referenceImageHeight(self, int a): - self.c_cuAmpcor.param.referenceImageHeight=a - @property - def referenceImageWidth(self): - return self.c_cuAmpcor.param.referenceImageWidth - @referenceImageWidth.setter - def referenceImageWidth(self, int a): - self.c_cuAmpcor.param.referenceImageWidth=a - @property - def secondaryImageHeight(self): - return self.c_cuAmpcor.param.secondaryImageHeight - @secondaryImageHeight.setter - def secondaryImageHeight(self, int a): - self.c_cuAmpcor.param.secondaryImageHeight=a - @property - def secondaryImageWidth(self): - return self.c_cuAmpcor.param.secondaryImageWidth - @secondaryImageWidth.setter - def secondaryImageWidth(self, int a): - self.c_cuAmpcor.param.secondaryImageWidth=a - - @property - def numberWindowDown(self): - return self.c_cuAmpcor.param.numberWindowDown - @numberWindowDown.setter - def numberWindowDown(self, int a): - self.c_cuAmpcor.param.numberWindowDown = a - @property - def numberWindowAcross(self): - return self.c_cuAmpcor.param.numberWindowAcross - @numberWindowAcross.setter - def numberWindowAcross(self, int a): - self.c_cuAmpcor.param.numberWindowAcross = a - @property - def numberWindows(self): - return self.c_cuAmpcor.param.numberWindows - - @property - def numberWindowDownInChunk(self): - return self.c_cuAmpcor.param.numberWindowDownInChunk - @numberWindowDownInChunk.setter - def numberWindowDownInChunk(self, int a): - self.c_cuAmpcor.param.numberWindowDownInChunk = a - @property - def numberWindowAcrossInChunk(self): - return self.c_cuAmpcor.param.numberWindowAcrossInChunk - @numberWindowAcrossInChunk.setter - def numberWindowAcrossInChunk(self, int a): - self.c_cuAmpcor.param.numberWindowAcrossInChunk = a - @property - def numberChunkDown(self): - return self.c_cuAmpcor.param.numberChunkDown - @property - def numberChunkAcross(self): - return self.c_cuAmpcor.param.numberChunkAcross - @property - def numberChunks(self): - return self.c_cuAmpcor.param.numberChunks - - ## gross offset - @property - def grossOffsetImageName(self): - return self.c_cuAmpcor.param.grossOffsetImageName.decode("utf-8") - @grossOffsetImageName.setter - def grossOffsetImageName(self, str a): - self.c_cuAmpcor.param.grossOffsetImageName = a.encode() - @property - def offsetImageName(self): - return self.c_cuAmpcor.param.offsetImageName.decode("utf-8") - @offsetImageName.setter - def offsetImageName(self, str a): - self.c_cuAmpcor.param.offsetImageName = a.encode() - - @property - def mergeGrossOffset(self): - return self.c_cuAmpcor.param.mergeGrossOffset - @mergeGrossOffset.setter - def mergeGrossOffset(self, int a): - self.c_cuAmpcor.param.mergeGrossOffset = a - - @property - def snrImageName(self): - return self.c_cuAmpcor.param.snrImageName.decode("utf-8") - @snrImageName.setter - def snrImageName(self, str a): - self.c_cuAmpcor.param.snrImageName = a.encode() - - @property - def covImageName(self): - return self.c_cuAmpcor.param.covImageName.decode("utf-8") - @covImageName.setter - def covImageName(self, str a): - self.c_cuAmpcor.param.covImageName = a.encode() - - @property - def referenceStartPixelDownStatic(self): - return self.c_cuAmpcor.param.referenceStartPixelDown0 - @referenceStartPixelDownStatic.setter - def referenceStartPixelDownStatic(self, int a): - self.c_cuAmpcor.param.referenceStartPixelDown0 = a - @property - def referenceStartPixelAcrossStatic(self): - return self.c_cuAmpcor.param.referenceStartPixelAcross0 - @referenceStartPixelAcrossStatic.setter - def referenceStartPixelAcrossStatic(self, int a): - self.c_cuAmpcor.param.referenceStartPixelAcross0 = a - @property - def grossOffsetDownStatic(self): - return self.c_cuAmpcor.param.grossOffsetDown0 - @grossOffsetDownStatic.setter - def grossOffsetDownStatic(self, int a): - self.c_cuAmpcor.param.grossOffsetDown0 =a - @property - def grossOffsetAcrossStatic(self): - return self.c_cuAmpcor.param.grossOffsetAcross0 - @grossOffsetAcrossStatic.setter - def grossOffsetAcrossStatic(self, int a): - self.c_cuAmpcor.param.grossOffsetAcross0 =a - - @property - def grossOffsetDownDynamic(self): - cdef int *c_data - c_data = self.c_cuAmpcor.param.grossOffsetDown - p_data = np.zeros(self.numberWindows, dtype = np.float32) - for i in range (self.numberWindows): - p_data[i] = c_data[i] - return p_data - @grossOffsetDownDynamic.setter - def grossOffsetDownDynamic (self, np.ndarray[np.int32_t,ndim=1,mode="c"] pa): - cdef int *c_data - cdef int *p_data - c_data = self.c_cuAmpcor.param.grossOffsetDown - p_data = pa.data - for i in range (self.numberWindows): - c_data[i] = p_data[i] - @property - def grossOffsetAcrossDynamic(self): - cdef int *c_data - c_data = self.c_cuAmpcor.param.grossOffsetAcross - p_data = np.zeros(self.numberWindows, dtype = np.float32) - for i in range (self.numberWindows): - p_data[i] = c_data[i] - return p_data - @grossOffsetAcrossDynamic.setter - def grossOffsetAcrossDynamic (self, np.ndarray[np.int32_t,ndim=1,mode="c"] pa): - cdef int *c_data - cdef int *p_data - c_data = self.c_cuAmpcor.param.grossOffsetAcross - p_data = pa.data - for i in range (self.numberWindows): - c_data[i] = p_data[i] - return - - - def setConstantGrossOffset(self, int goDown, int goAcross): - """ - constant gross offsets - param goDown gross offset in azimuth direction - param goAcross gross offset in range direction - """ - self.c_cuAmpcor.param.setStartPixels(self.referenceStartPixelDownStatic, self.referenceStartPixelAcrossStatic, goDown, goAcross) - - def setVaryingGrossOffset(self, np.ndarray[np.int32_t,ndim=1,mode="c"] vD, np.ndarray[np.int32_t,ndim=1,mode="c"] vA): - """ - varying gross offsets for each window - param vD numpy 1d array of size numberWindows, gross offsets in azimuth direction - param vA numpy 1d array of size numberWindows, gross offsets in azimuth direction - static part should be included - """ - self.c_cuAmpcor.param.setStartPixels(self.referenceStartPixelDownStatic, self.referenceStartPixelAcrossStatic, vD.data, vA.data) - - def checkPixelInImageRange(self): - """ check whether each window is with image range """ - self.c_cuAmpcor.param.checkPixelInImageRange() - - def setupParams(self): - """ - set up constant parameters and allocate array parameters (offsets) - should be called after number of windows is set and before setting varying gross offsets - """ - self.c_cuAmpcor.param.setupParameters() - - def runAmpcor(self): - """ main procedure to run ampcor """ - self.c_cuAmpcor.runAmpcor() - - -# end of file