Skip to content

Factorized backprojection - #346

Open
bhawkins wants to merge 253 commits into
isce-framework:developfrom
bhawkins:factorized_bp
Open

bhawkins wants to merge 253 commits into
isce-framework:developfrom
bhawkins:factorized_bp

Conversation

@bhawkins

@bhawkins bhawkins commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

This PR adds a factorized backprojection (FBP) algorithm for SAR focusing as a faster alternative to the existing direct backprojection method.

Algorithm Design

The basic idea is from Yegulalp 1999 where local, low-resolution polar grids can efficiently store partial sums of subapertures. As in other papers, the idea is extended hierarchically, so that multiple local grids can be combined together into a higher resolution grid. This process can be repeated in successive stages until the desired resolution is met. In the limit one achieves a factor of $O(N / log(N))$ reduction in the number of sums per output pixel (where $N$ is the number of pulses in the synthetic aperture). In practice there's a fair amount of overhead, but the speedup is still significant.

The FBP algorithm is usually described for the spotlight case, and the one here has a few modifications for the stripmap case relevant to NISAR. First, the azimuth extent of the local grid is extended based on the duration of the subaperture, since the beam is sliding. Second, the azimuth resolution of the local grid is not allowed to exceed the L/2 limit. The radar travel time between transmit and receive ("bistatic" correction) is accounted for both in the initial back projection stage and in the selection of grid coordinates. The latter is necessary to maintain a baseband signal in azimuth.

I'm not sure I'd call this a "fast factorized" (FFBP) algorithm. In the interest of maximal accuracy, it does full 2D interpolation and makes no assumptions about the trajectory or nesting of coordinate grids. Non-uniform FFT algorithms are used for interpolation in order to maintain high accuracy with small kernels. The zero-padded transforms do occupy a fair amount of memory, though, which may be a bottleneck. Care has been taken to ensure that only the "active" subimages reside in memory at any given time.

You might notice a few new functions that are not used in the NISAR RSLC workflow. These have been used for processing UAVSAR data from circular tracks (CSAR).

Key Features

  • Factorized backprojection algorithm: Multi-stage approach (initial → middle → final) that reduces computational complexity compared to direct backprojection
  • 2D NFFT implementation: New CPU and GPU implementations of 2D Non-uniform FFT (NFFT2d) required by the FBP algorithm
  • GPU acceleration: CUDA implementations for all FBP stages and NFFT2d operations
  • Bug fixes in interp1d and interp2d
  • Python interface: New azcomp_bp and azcomp_fbp functions exposed via isce3.focus.azcomp_bp module
  • Configuration support: Added FBP parameters to focus workflow configuration schema

Usage

The algorithm is highly configurable. You can specify the number of stages, the size of each stage, and the interpolation parameters for each stage (NFFT kernel size and oversampling ratio in both dimensions). To use a two-stage algorithm, the RSLC configuration file should look like

runconfig:
    groups:
        processing:
            azcomp:
                factorization:
                - size: 128

which means sum groups of 128 pulses into polar grids before summing those into the output image. Similarly,

# runconfig.groups.processing.azcomp
                factorization:
                - size: 64
                - size: 2

means a three-stage algorithm where the initial grids comprise 64 pulses each, these get merged together two at a time, and then the merged grids get summed into the output image. The default factorization: [{"size": 1}] is understood to mean the original direct backprojection algorithm.

In my testing I've found the two-stage n=128 algorithm is a 10x speedup of the azcomp algorithm on a g6e instance in AWS EC2.

I included a script that can generate animations like the following from the FBP debug file:

factors.mp4

bhawkins and others added 6 commits August 6, 2026 00:51
Change accumulatePolarImagesToRadarGrid and mergePolarImages to use
std::vector<const NFFT2dResult<float>*> instead of std::vector<NFFT2dResult<float>>
to match the GPU implementation and avoid unnecessary copies.

Updated Python bindings to accept py::sequence and convert to vector
of pointers, consistent with the GPU bindings.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@bhawkins

bhawkins commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

All the CI failures are in test.cxx.isce3.io.raster.raster with

[ RUN      ] RasterTest.addRasterToVRT
ERROR 3: Failed to read scanline 100.
ERROR 1: IReadBlock failed at X offset 0, Y offset 100: Failed to read scanline 100.
In isce3::io::Raster::getSetValue() - error in RasterIO.
/home/runner/work/isce3/isce3/tests/cxx/isce3/io/raster/raster.cpp:258: Failure
Expected equality of these values:
  std::isfinite(val)
    Which is: false
  true

[  FAILED  ] RasterTest.addRasterToVRT (55 ms)

Seems unrelated to the PR and the test passes locally.

@bhawkins bhawkins left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments from today's review meeting.

Comment thread python/packages/isce3/focus/serialization.py
Comment thread python/packages/isce3/focus/azcomp_bp.py Outdated
Comment thread python/packages/isce3/focus/azcomp_bp.py Outdated
Comment thread python/packages/isce3/focus/azcomp_bp.py Outdated
Comment thread python/packages/isce3/focus/azcomp_bp.py Outdated
bhawkins and others added 5 commits August 6, 2026 20:53
Documented all functions and classes in serialization.py with NumPy format
docstrings including parameter types, return values, and attribute descriptions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@bhawkins

bhawkins commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Fix for the CI failure is in #348

@hfattahi hfattahi added this to the R05.03.0 milestone Aug 11, 2026
@bhawkins

bhawkins commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

I just pushed a change making FBP the default algorithm in order to simplify PCM/SDS testing. @hfattahi @jshimada47

@jshimada47

Copy link
Copy Markdown
Contributor

I'm getting a unit test failure while doing the build on dev-5 in test.python.pkg.nisar.workflows.focus:

253/280 Test #248: test.python.pkg.nisar.workflows.focus ...............................***Failed   23.63 sec
============================= test session starts ==============================
platform linux -- Python 3.12.11, pytest-8.4.1, pluggy-1.5.0
rootdir: /src
configfile: pyproject.toml
collected 3 items

../../../../../../src/tests/python/packages/nisar/workflows/focus.py .F. [100%]

=================================== FAILURES ===================================
__________________________ test_focus[factorization1] __________________________

factorization = [<nisar.workflows.focus.Struct object at 0x7eff92c080b0>]

    @pytest.mark.parametrize("factorization", (direct_bp, factorized_bp_64))
    def test_focus(factorization):
        cfg = get_test_cfg()
        cfg.runconfig.groups.processing.azcomp.factorization = factorization

>       focus.focus(cfg)

/src/tests/python/packages/nisar/workflows/focus.py:45:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/bld/install/opt/conda/lib/python3.12/site-packages/nisar/workflows/focus.py:1873: in focus
    slc = SLC(output_slc_path, mode="w", product=product,
/bld/install/opt/conda/lib/python3.12/site-packages/nisar/products/writers/SLC.py:353: in __init__
    super().__init__(*args, **kw)
/opt/conda/lib/python3.12/site-packages/h5py/_hl/files.py:564: in __init__
    fid = make_fid(name, mode, userblock_size, fapl, fcpl, swmr=swmr)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/conda/lib/python3.12/site-packages/h5py/_hl/files.py:244: in make_fid
    fid = h5f.create(name, h5f.ACC_TRUNC, fapl=fapl, fcpl=fcpl)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
h5py/_objects.pyx:56: in h5py._objects.with_phil.wrapper
    ???
h5py/_objects.pyx:57: in h5py._objects.with_phil.wrapper
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   OSError: Unable to synchronously create file (unable to truncate a file which is already open)

h5py/h5f.pyx:122: OSError
------------------------------ Captured log call -------------------------------
WARNING  focus:focus.py:331 No DEM given, using ref height 0.0 (m).
WARNING  focus:focus.py:441 Desired Doppler LUT start time is 1.0 seconds before ephemeris start. Consider adjusting ephemeris_crop_pad or providing more orbit/attitude data.
WARNING  focus:focus.py:445 Desired Doppler LUT end time is 1.0 seconds after ephemeris end. Consider adjusting ephemeris_crop_pad or providing more orbit/attitude data.
=============================== warnings summary ===============================
tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
tests/python/packages/nisar/workflows/focus.py::test_focus[factorization1]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/products/readers/Base/Identification.py:238: UserWarning: Could not find isJointObservation in product identification.
    warn("Could not find isJointObservation in product identification.")

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
tests/python/packages/nisar/workflows/focus.py::test_focus[factorization1]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/products/readers/Base/Identification.py:247: UserWarning: Could not find hasInputDataException in product identification.  Assuming there are no anomalies.
    warn("Could not find hasInputDataException in product "

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/products/readers/Raw/Raw.py:466: UserWarning: Missing dataset /science/LSAR/RRSD/highRateTelemetry/txH/rxH/QFSP0/CHIRP_CORRELATOR_I1_H01 in /src/tests/data/focus/REE_L0B_out17.h5. Detailed error -> 'Unable to synchronously open object (component not found)'
    warn(

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/antenna/rx_channel_imbalance_helpers.py:212: UserWarning: No LNA CAL to represent RX! Use BYPASS Cal instead!
    warn('No LNA CAL to represent RX! Use BYPASS Cal instead!')

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/antenna/rx_channel_imbalance_helpers.py:217: UserWarning: No LNA or BYPASS CAL! LNA mean will be all unity. The results will be invalid!
    warn('No LNA or BYPASS CAL! LNA mean will be all unity. '

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/antenna/rx_channel_imbalance_helpers.py:496: UserWarning: All values are zero for HH-pol Caltone! They are set to untiy. Result may be invalid!
    warn(f'All values are zero for {msg}! They are set to untiy. '

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/antenna/rx_channel_imbalance_helpers.py:500: UserWarning: Some values are zero for HH-pol Caltone!
    warn(f'Some values are zero for {msg}!')

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/antenna/rx_channel_imbalance_helpers.py:48: UserWarning: The size of LNA-CALTONE ratio is 1 instead of 12!
    warn('The size of LNA-CALTONE ratio is '

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/products/readers/instrument/instrument_parser.py:347: MissingInstrumentFieldWarning: HPOL/channelAdjustment/
    return self._channel_adjustment_factors('rx', pol)

tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
  /bld/install/opt/conda/lib/python3.12/site-packages/nisar/products/readers/instrument/instrument_parser.py:310: MissingInstrumentFieldWarning: HPOL/channelAdjustment/
    return self._channel_adjustment_factors('tx', pol)

tests/python/packages/nisar/workflows/focus.py::test_schema
  <unknown>:1: SyntaxWarning: invalid escape sequence '\d'

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
==================================== PASSES ====================================
__________________________ test_focus[factorization0] __________________________
----------------------------- Captured stdout call -----------------------------
{
  "magnitude": 0.00012279492511879653,
  "phase": -3.09582781791687,
  "azimuth": {
    "ISLR": -15.777904987335205,
    "PSLR": -17.766033411026,
    "resolution": 1.203125,
    "index": 64.0,
    "offset": 0.0007591966132167727,
    "phase ramp": -0.006026060786098242
  },
"range": {
    "ISLR": -13.799757957458496,
    "PSLR": -16.547774076461792,
    "resolution": 1.15625,
    "index": 64.0,
    "offset": 4.2870595962085645e-10,
    "phase ramp": -0.015377550385892391
  }
}
------------------------------ Captured log call -------------------------------
WARNING  focus:focus.py:331 No DEM given, using ref height 0.0 (m).
WARNING  focus:focus.py:441 Desired Doppler LUT start time is 1.0 seconds before ephemeris start. Consider adjusting ephemeris_crop_pad or providing more orbit/attitude data.
WARNING  focus:focus.py:445 Desired Doppler LUT end time is 1.0 seconds after ephemeris end. Consider adjusting ephemeris_crop_pad or providing more orbit/attitude data.
WARNING  SLCWriter:SLC.py:333 Could not determine coverage indicator given frame_polygon=<POLYGON EMPTY> and image_polygon=<POLYGON Z ((-54.584 3.173 0, -54.583 3.174 -7.081e-10, -54.582 3.174 0, -54...>
WARNING  SLCWriter:SLC.py:771 Input L0B has undesired 'units' attribute on diagnosticModeFlag dataset.  Will omit from output RSLC.
WARNING  focus:focus.py:2188 No noise-only range lines within the specified pulse interval. Skip noise estimation and set noise equivalent backscatter to zero.
=========================== short test summary info ============================
FAILED ../../../../../../src/tests/python/packages/nisar/workflows/focus.py::test_focus[factorization1]
PASSED ../../../../../../src/tests/python/packages/nisar/workflows/focus.py::test_focus[factorization0]
PASSED ../../../../../../src/tests/python/packages/nisar/workflows/focus.py::test_schema
================== 1 failed, 2 passed, 13 warnings in 23.14s ===================
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: No reference epoch provided. Using first date time from XML file as orbit reference epoch.
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: cube height: 20
journal:
 -- cube length: 12
 -- cube width: 12
journal: EPSG: 32621
journal: estimating the ground-track velocity using rdr2geo
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: No reference epoch provided. Using first date time from XML file as orbit reference epoch.
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification
journal: hasInputDataException not found at /science/LSAR/identification

        Start 249: test.python.pkg.nisar.workflows.gen_doppler_range_product
254/280 Test #249: test.python.pkg.nisar.workflows.gen_doppler_range_product ...........   Passed    3.58 sec

@Tyler-g-hudson Tyler-g-hudson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slowly chewing my way through the PR, here are a very basic review item and question

Comment thread python/extensions/pybind_isce3/cuda/focus/Backproject.cu
Comment on lines +449 to +452
# List of aperture factorization parameters.
# Default to two-stage with 128 pulses per subaperture.
factorization:
- size: 128

@Tyler-g-hudson Tyler-g-hudson Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, do we intend to modify this list to be hierarchical later on for more efficient processing or no?

Also, for a typical input L0B ~40 seconds per RSLC containing ~76k pulses, wouldn't 256 be the nearest power of 2 to the optimum per Yegulalp? Have you had a chance to check this case? Are my numbers wrong here? (I'm basing them off of a single full-length RSLC I have, and assuming a 1.5 second pad before and after in the L0B at the L0B native PRF)

Do we need additional scaled testing for hierarchical runs? I could produce some scaled workflows on a g6e if you have one or more RSLCs you want me to reproduce and a handful of factorization lists.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recall that we're using stripmap mode (not spotlight), so there are only around 6000 pulses in a NISAR synthentic aperture. Then Yegulalp's sqrt(num_aperture) suggestion for a two-stage algorithm would work out to 77 pulses.

However, in the version here (at least in the default config) the first stage uses a 9-point 1D interpolator while the second (and higher) stage(s) use a 5x5=25 point 2D interpolator. So if interpolation dominates the runtime, then later stages are around f=2.7 times more expensive than the first stage. To split the work evenly among a two-stage algorithm, then you'd want to solve num_aperture = (f * n) * n which results in around f * n = 129 pulses in the first stage. In the general case you'd want to solve

num_aperture = f**(num_stages - 1) * n**num_stages

for n. So for a NISAR 3-stage algorithm you might try the config

factorization:
- size: 70
- size: 9

However, there are other considerations like the geometry calculations (including the fact that the geometry is in float64 while the interpolation is float32), the FFT overhead, GPU memory bandwidth, cache sizes, and so-on that complicate the picture. I measured runtimes for two-stage cases using size in {64, 128, 256} and didn't see very large differences between them on g6/g6e instances. It might be worth repeating that experiment since I've hacked on the code a fair amount since then.

bhawkins and others added 2 commits September 10, 2026 16:49
py_image_interpolators elements were only borrowed via a transient
py::object during std::transform, leaving no owning reference once the
GIL was released in accumulate_polar_images_to_radar_grid. Another
thread could drop the last reference to an element while the C++ code
held a raw pointer into it. Materialize an owning std::vector<py::object>
before releasing the GIL, and apply the same pattern in
merge_polar_images for consistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Now that image_interpolators elements are held via owning py::object
references, it's safe to release the GIL during the potentially
long-running mergePolarImages call, matching the pattern already used
in accumulate_polar_images_to_radar_grid.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@Tyler-g-hudson Tyler-g-hudson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions and naming/documentation recommendations

# NOTE We'll actually just define the tasks and only evaluate them as needed
# in order to reduce memory pressure. We could process them in
# parallel using concurrent.futures or dask, for for now just store them in
# a dict keyed by the PolarGrid associated with the imagelets.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would Python-level parallelism even help? The individual tasks are parallel at the C++/OMP level so, at least for CPU, they should saturate the available computation resources, no?

This would probably be more complicated to figure out for GPU, but you'd need to see if the GPU usage is low enough at any step of the task to justify parallel task requests and associated complexity. Maybe worth a test later.

_, img, _ = bp_to_polar_grid(fdata, sr, x, v,
polar_grid, dem, fc, kernel, "nodelay", rdr2geo_params)
if debugfile is not None:
import h5py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend moving this to the top of the file, I don't think this is a heavy dependency relative to ISCE3 and should only be performed once as the file is uploaded if it has not already been imported (and it likely has)

Comment thread cxx/isce3/signal/NFFT2d.h
Comment on lines +65 to +66
template <typename T>
class NFFT2dResult {

@Tyler-g-hudson Tyler-g-hudson Sep 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Readability suggestion:

Suggested change
template <typename T>
class NFFT2dResult {
/**
* Prepared interpolator for 2D image queries at arbitrary positions.
* Holds oversampled grid enabling rapid Kaiser-Bessel interpolation
* queries.
*/
template <typename T>
class KaiserBesselInterpolator2d {

I think this could use a rename and/or or some nearby documentation. My reason for suggesting this is that the data it holds is in the polar domain (Seeing NFFT in the name might make a reader mistake at a glance that this as some sort of frequency spectrum) and that, in principle, an oversampler of any kind could produce one of these interpolators (presumably in any domain?) so long as it can also produce the correct kernels.

In an ideal world I'd recommend moving this to a separate file and renaming it - failing those, a quick comment here would probably help future maintenance.

I'm interested in this method in general - do you think it's practical for InSAR resampling? Do you know any pros and cons to doing so off the top of your head?

Comment thread cxx/isce3/signal/NFFT2d.h
Comment on lines +135 to +151
/**
* @brief Create an NFFT2d object for interpolating an image.
*
* @tparam T Format of real/imag pixel data, typically float or double
* @param image Input time-domain image. A temporary copy will be made if
* it is not row-major with a column stride of one.
* @param m Half-length of interpolator along {rows, columns}
* @param s Minimum factors (> 1) for frequency-domain zero-padding
* along {rows, columns}. Actual padding may be larger to
* achieve efficient inverse transform size.
* @param pad_input Whether to also zero-pad input data to an efficient
* forward transform size. Requires extra memory.
*
* @return NFFT2d<T> object for interpolating the image.
*/
template<typename T>
NFFT2dResult<T> makeImageNFFT2d(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above - this no longer returns an NFFT2d so I recommend a rename for it. Docstring parameters also look to have been changed.

Suggested change
/**
* @brief Create an NFFT2d object for interpolating an image.
*
* @tparam T Format of real/imag pixel data, typically float or double
* @param image Input time-domain image. A temporary copy will be made if
* it is not row-major with a column stride of one.
* @param m Half-length of interpolator along {rows, columns}
* @param s Minimum factors (> 1) for frequency-domain zero-padding
* along {rows, columns}. Actual padding may be larger to
* achieve efficient inverse transform size.
* @param pad_input Whether to also zero-pad input data to an efficient
* forward transform size. Requires extra memory.
*
* @return NFFT2d<T> object for interpolating the image.
*/
template<typename T>
NFFT2dResult<T> makeImageNFFT2d(
/**
* @brief Create an rapid interpolator using an intermediate NFFT2d.
*
* @tparam T Format of real/imag pixel data, typically float or double
* @param image Input time-domain image. A temporary copy will be made if
* it is not row-major with a column stride of one.
* @param params parameters for the intermediate NFFT
* @param pad_input Whether to also zero-pad input data to an efficient
* forward transform size. Requires extra memory.
*
* @return KaiserBesselInterpolator2d<T> object for interpolating the image.
*/
template<typename T>
KaiserBesselInterpolator2d<T> makeFastImageInterpolator(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GPU Enable GPU builds for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants