Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

force overview dataset for WarpedVRT #132

Closed
wants to merge 5 commits into from
Closed

Conversation

vincentsarago
Copy link
Member

@vincentsarago vincentsarago commented Nov 22, 2019

This is a major change

While working on #95 and RemotePixel/amazonlinux#10 I realized that maybe we were using WarpedVRT in an unoptimized way, when (comparing warped kernel + GET requests)

This goes back to rasterio/rasterio#1373 and precisely rasterio/rasterio#1373 (comment)

When looking at the gdalwarp command I realized the gdal use specific overview level to create the output dataset. This PR tries to implement such logic.

The first tests show that GET requests and output results are similar to gdalwarp outputs.

@sgillies I'll need your help on this one (and I believe this will also fix the performance downgrade I saw in RemotePixel/amazonlinux#10). (Edit the performance downgrade was due to SQLITE3 and centos)
I'm not sure if this could also go directly to rasterio code base. (I know you have ton of stuff on your side so no stress, I'll continue to run some tests on my side).

tagging @dionhaefner @mojodna because it might be of your interest.

options = {"overview_level": overview_level} if overview_level is not None else {}
with rasterio.open(src_dst.name, **options) as ovr_dst:
w, s, e, n = bounds
vrt_transform = transform.from_bounds(w, s, e, n, tilesize, tilesize)
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the main change.
Instead of relying on WarpedVRT, we force rasterio to use a specific overview level (or the highest resolution)

Note, we cannot pass overview_level=None to rasterio because it somehow get converted to overview_level=0

This comment was marked as resolved.

This comment was marked as resolved.

@vincentsarago
Copy link
Member Author

Notes:

Old Rio-Tiler

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "VSICURL"
VSICURL: Downloading 0-16383 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 60404072-60597466 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 61235630-61435374 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "Kernel"
GDALWarpKernel()::GWKOpenCLCase() Src=4702,7593,294x199 Dst=0,0,263x128
GDALWarpKernel()::GWKOpenCLCase() Src=4669,7719,294x199 Dst=0,128,263x128
GDALWarpKernel()::GWKOpenCLCase() Src=4658,7836,281x98 Dst=0,256,263x7

New Rio-Tiler

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "VSICURL"
VSICURL: Downloading 0-16383 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 60404072-60597466 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 61235630-61435374 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "Kernel"
GDALWarpKernel()::GWKOpenCLCase() Src=4701,7593,295x202 Dst=0,0,256x128
GDALWarpKernel()::GWKOpenCLCase() Src=4667,7723,295x202 Dst=0,128,256x128

GDAL_DISABLE_READDIR_ON_OPEN

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_CURL_VERBOSE=TRUE CPL_DEBUG=ON gdalwarp -t_srs EPSG:3857 -te -13423565.159329513 4187526.157575096 -13413781.219709009 4197310.097195597 -ts 256 256 -r bilinear /vsicurl/https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif 12-676-1619_gdal.tif 2>&1 | grep "Kernel"

VSICURL: Downloading 0-16383 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 60404072-60597466 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 61235630-61435374 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_CURL_VERBOSE=TRUE CPL_DEBUG=ON gdalwarp -t_srs EPSG:3857 -te -13423565.159329513 4187526.157575096 -13413781.219709009 4197310.097195597 -ts 256 256 -r bilinear /vsicurl/https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif 12-676-1619_gdal.tif 2>&1 | grep "VSICURL"
GDAL: GDALWarpKernel()::GWKOpenCLCase() Src=4667,7593,329x332 Dst=0,0,256x256

Note:

  • the new rio-tiler gives the exact same results as gdalwarp while the old version has some differences
  • the 3 versions uses the same number of GET and data ranges (fetch the higest resolution)
import sys
import logging

import mercantile

import rasterio
from rasterio import transform

from rio_tiler.utils import array_to_image, _tile_read

logging.basicConfig(stream=sys.stderr, level=10)


def _rio_tiler_vrt(resampling, bounds, tilesize=256):
    with rasterio.open(input) as src_dst:
        tile, _ = _tile_read(
            src_dst,
            bounds,
            tilesize=tilesize,
            resampling_method=resampling,
            tile_edge_padding=0,
        )

    dst_crs = {'init': 'EPSG:3857'}
    w, s, e, n = bounds
    dst_transform = transform.from_bounds(w, s, e, n, tilesize, tilesize)
    with open(f"{tile_z}-{tile_x}-{tile_y}_{resampling}.tif", "wb") as f:
        f.write(array_to_image(
            tile,
            dtype=tile.dtype,
            img_format="GTiff",
            crs=dst_crs,
            transform=dst_transform
        ))


if __name__ == '__main__':
    input = "https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif"
    tile_z = 12
    tile_x = 676
    tile_y = 1619

    tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z)
    tile_bounds = mercantile.xy_bounds(tile)
    _rio_tiler_vrt("bilinear", tile_bounds, tilesize=256)

@vincentsarago
Copy link
Member Author

Note2 (fetching overview)

Old rio-tiler

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "VSICURL"
VSICURL: Downloading 0-16383 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 3992502-4196404 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 4816566-5018091 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 5566692-5746627 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "Kernel"
GDALWarpKernel()::GWKOpenCLCase() Src=1119,1404,376x221 Dst=0,0,350x128
GDALWarpKernel()::GWKOpenCLCase() Src=1085,1529,376x221 Dst=0,128,350x128
GDALWarpKernel()::GWKOpenCLCase() Src=1060,1654,368x188 Dst=0,256,350x94

New Rio-tiler

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "VSICURL"
VSICURL: Downloading 0-16383 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 3992502-4196404 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 4816566-5018091 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 5566692-5746627 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...

$ GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR CPL_DEBUG=ON python test.py 2>&1 | grep "Kernel"
GDALWarpKernel()::GWKOpenCLCase() Src=1106,1403,390x269 Dst=0,0,256x128
GDALWarpKernel()::GWKOpenCLCase() Src=1059,1574,391x269 Dst=0,128,256x128

GDAL

$ CPL_DEBUG=ON gdalwarp -t_srs EPSG:3857 -te -13501836.676293533 4265797.674539115 -13462700.917811522 4304933.433021125 -ts 256 256 -r bilinear /vsicurl/https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif 10-167-402_bilinear_gdal.tif 2>&1 | grep "VSICURL: Downloading "
VSICURL: Downloading 0-16383 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 3992502-4196404 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 4816566-5018091 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...
VSICURL: Downloading 5566692-5746627 (https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif)...

$ CPL_DEBUG=ON gdalwarp -t_srs EPSG:3857 -te -13501836.676293533 4265797.674539115 -13462700.917811522 4304933.433021125 -ts 256 256 -r bilinear /vsicurl/https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif 10-167-402_bilinear_gdal.tif 2>&1 | grep "GDALWarpKernel"
GDAL: GDALWarpKernel()::GWKOpenCLCase() Src=1060,1404,435x438 Dst=0,0,256x256

Again, both new and old fetch the same amount of data but the new rio-tiler gives the exact same result as the gdalwarp command!



import sys
import logging

import mercantile

import rasterio
from rasterio import transform

from rio_tiler.utils import array_to_image, _tile_read

logging.basicConfig(stream=sys.stderr, level=10)


def _rio_tiler_vrt(resampling, bounds, tilesize=256):
    with rasterio.open(input) as src_dst:
        tile, _ = _tile_read(
            src_dst,
            bounds,
            tilesize=tilesize,
            resampling_method=resampling,
            tile_edge_padding=0,
        )

    dst_crs = {'init': 'EPSG:3857'}
    w, s, e, n = bounds
    dst_transform = transform.from_bounds(w, s, e, n, tilesize, tilesize)
    with open(f"{tile_z}-{tile_x}-{tile_y}_{resampling}.tif", "wb") as f:
        f.write(array_to_image(
            tile,
            dtype=tile.dtype,
            img_format="GTiff",
            crs=dst_crs,
            transform=dst_transform
        ))


if __name__ == '__main__':
    input = "https://s3-us-west-2.amazonaws.com/remotepixel-pub/data/Int16_nodata9999.tif"
    # tile_z = 12
    # tile_x = 676
    # tile_y = 1619

    tile_z = 10
    tile_x = 167
    tile_y = 402

    tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z)
    tile_bounds = mercantile.xy_bounds(tile)
    _rio_tiler_vrt("bilinear", tile_bounds, tilesize=256)

@vincentsarago
Copy link
Member Author

Note: started a quick benchmarking suite over https://github.com/vincentsarago/rio-tiler-bench

For now it shows real good performances with this new update

@vincentsarago
Copy link
Member Author

Note: The tests are failing because we are trying to re-open a WarpedVRT dataset (sentinel-1). Not sure what's the best path but maybe I could spend some time to see if rasterio/rasterio#1504 can be a solution.
like instead of doing:

with rasterio.open(src) as src_dst:
    ovr = get_overview_level(...)
    with rasterio.open(src_dst.name, overview_level=ovr) as ovr_dst:
    .... 

we could do

with rasterio.open(src) as src_dst:
    ovr = get_overview_level(...)
    ovr_dst = src_dst.overview(ovr) if ovr else src_dst
    .... 

@dionhaefner
Copy link

Is this only an issue for rasters with an internal mask?

@vincentsarago
Copy link
Member Author

vincentsarago commented Nov 24, 2019

Is this only an issue for rasters with an internal mask?

@dionhaefner no it's not, this is more a general fix because I think the way we were building the WarpedVRT before is not optimal!

The main motivation was to fix performances saw with GDAL3 but also to try to get the same results than with gdalwarp commands

@vincentsarago

This comment has been minimized.

@dionhaefner
Copy link

dionhaefner commented Nov 26, 2019

Benchmark results on Terracotta: (GDAL 2)

Current master

--------------------------- benchmark 'test_bench_singleband': 16 tests ----------------------------
Name (time in ms)                                Min                Max             Median
----------------------------------------------------------------------------------------------------
test_bench_singleband[subpixel-nearest]      11.1776 (1.0)      16.1220 (1.0)      11.8393 (1.03)
test_bench_singleband[subpixel-average]      11.2208 (1.00)     29.9115 (1.86)     11.4441 (1.0)
test_bench_singleband[subpixel-linear]       15.5467 (1.39)     21.9950 (1.36)     15.9134 (1.39)
test_bench_singleband[subpixel-cubic]        15.7889 (1.41)     20.0737 (1.25)     16.1028 (1.41)
test_bench_singleband[balanced-nearest]      20.2816 (1.81)     21.4078 (1.33)     20.7876 (1.82)
test_bench_singleband[preview-nearest]       20.4347 (1.83)     23.2132 (1.44)     20.7596 (1.81)
test_bench_singleband[balanced-average]      22.8872 (2.05)     56.2267 (3.49)     28.4317 (2.48)
test_bench_singleband[preview-linear]        23.0911 (2.07)     24.4024 (1.51)     23.3740 (2.04)
test_bench_singleband[preview-average]       23.4113 (2.09)     31.0901 (1.93)     24.4780 (2.14)
test_bench_singleband[balanced-linear]       23.7165 (2.12)     31.7659 (1.97)     24.4105 (2.13)
test_bench_singleband[preview-cubic]         26.8740 (2.40)     36.4770 (2.26)     29.8800 (2.61)
test_bench_singleband[balanced-cubic]        27.2988 (2.44)     35.8890 (2.23)     34.4080 (3.01)
test_bench_singleband[birds-eye-nearest]     33.1614 (2.97)     68.6241 (4.26)     33.9744 (2.97)
test_bench_singleband[birds-eye-average]     40.8274 (3.65)     44.6856 (2.77)     42.0957 (3.68)
test_bench_singleband[birds-eye-cubic]       45.3892 (4.06)     62.9233 (3.90)     59.4120 (5.19)
test_bench_singleband[birds-eye-linear]      53.7117 (4.81)     59.6658 (3.70)     56.9833 (4.98)
----------------------------------------------------------------------------------------------------

Passing overview_level

--------------------------- benchmark 'test_bench_singleband': 16 tests ----------------------------
Name (time in ms)                                Min                Max             Median
----------------------------------------------------------------------------------------------------
test_bench_singleband[subpixel-nearest]      13.2647 (1.0)      16.5845 (1.0)      13.5784 (1.0)
test_bench_singleband[subpixel-average]      13.3445 (1.01)     34.0068 (2.05)     13.6193 (1.00)
test_bench_singleband[subpixel-linear]       17.6515 (1.33)     19.1525 (1.15)     17.9487 (1.32)
test_bench_singleband[subpixel-cubic]        17.9973 (1.36)     22.9039 (1.38)     18.3362 (1.35)
test_bench_singleband[preview-nearest]       21.7862 (1.64)     25.5545 (1.54)     22.2283 (1.64)
test_bench_singleband[balanced-nearest]      22.0967 (1.67)     31.0465 (1.87)     22.5971 (1.66)
test_bench_singleband[balanced-average]      24.4480 (1.84)     48.4540 (2.92)     25.0515 (1.84)
test_bench_singleband[preview-average]       24.5718 (1.85)     28.2534 (1.70)     25.1858 (1.85)
test_bench_singleband[preview-linear]        25.1513 (1.90)     29.3598 (1.77)     25.5643 (1.88)
test_bench_singleband[balanced-linear]       25.3467 (1.91)     33.9734 (2.05)     31.6715 (2.33)
test_bench_singleband[preview-cubic]         27.0620 (2.04)     30.7562 (1.85)     27.5491 (2.03)
test_bench_singleband[balanced-cubic]        28.4580 (2.15)     37.7537 (2.28)     30.6003 (2.25)
test_bench_singleband[birds-eye-nearest]     33.3614 (2.52)     73.1961 (4.41)     35.2414 (2.60)
test_bench_singleband[birds-eye-average]     33.4381 (2.52)     37.6632 (2.27)     34.0248 (2.51)
test_bench_singleband[birds-eye-cubic]       37.2590 (2.81)     51.8554 (3.13)     38.3483 (2.82)
test_bench_singleband[birds-eye-linear]      43.7494 (3.30)     48.1977 (2.91)     45.6189 (3.36)
----------------------------------------------------------------------------------------------------

Definitely defuses the expensive birds-eye cases a bit, but overall not too far above noise level.

Edit: Actually, the only difference is the birds-eye cases when using interpolation (not nearest), which are about 20% faster in the new version.

@vincentsarago
Copy link
Member Author

Well I'm going to close this.

Rasterio WarpedVRT is doing the right thing and fetch the overview, the problem I was seeing (difference in kernel) due to the padding options and should then be removed to use SOURCE_EXTRA

@vincentsarago
Copy link
Member Author

🤔 this is not over in fact, I was able to confirm that forcing the process on the overview was giving the same result as with non-force overview for UInt16 dataset ... but when using Int16 dataset then I see differences.

-> https://gist.github.com/vincentsarago/9951d1aab15359ee826b3b4c2cebeeab

@sgillies This might be a pure resampling problem and not really a WarpedVRT thing. For now I'm going to remove the padding option (which was causing most of the problem) and document the use of SOURCE_EXTRA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

3 participants