Package: django-large-image
🩻 🗺️ django-large-imagedjango-large-imageis an abstraction oflarge-imagefor use withdjango-rest-frameworkproviding viewset mixins for endpoints to
work with large images (Cloud Optimized GeoTiffs or medical image formats) in
Django. The dynamic tile server provided here prevents the need for
preprocessing large images into tile sets for viewing interactively on
slippy-maps. Under the hood, large-image applies operations (rescaling,
reprojection, image encoding) to create image tiles on-the-fly.Lightning Talk for 2022 Cloud-Native Geospatial Outreach EventView slides hereTable of ContentsOverviewSupportFeaturesInstallationUsageExample CodeCustomizationNon-Detail ViewSetsStylingConverting Images to Pyramidal Tiffs (COGs)Using with django-rasterDemo Appℹ️ OverviewThis package brings Kitware'slarge-imageto Django by providing a set of abstract, mixin API viewset classes that will
handle tile serving, fetching metadata from images, and extracting regions of
interest.django-large-imageis an installable Django app with
a few classes that can be mixed into a Django project (or application)'s
drf-based viewsets to provide tile serving endpoints out of the box. Notably,django-large-imageis designed to work specifically withFileFieldinterfaces with development being tailored to Kitware'sS3FileField. GeoDjango'sGDALRastercan also be used by returningGDALRaster.namein theget_path()override.This package ships with pre-made HTML templates for rendering geospatial image
tiles with CesiumJS and non-geospatial image tiles withGeoJS.Dynamic tile server in Django built  on top of large-image (and GDAL)🤝 Supportdjango-large-imageand the supportinglarge-imagelibrary are developed and  maintained by the Data & Analytics group atKitware, Inc.We work with large image data in both the geospatial and medical capacities.
If you have questions about these technologies, or you would like to discuss
your own geospatial and medical image problems and learn how we can help,
please reach out atkitware@kitware.com. We look forward to the conversation!🌟 FeaturesRich set of RESTful endpoints to extract information from large image formats:Image metadata (/info/metadata,/info/metadata_internal)Tile serving (/tiles/{z}/{x}/{y}.png?projection=EPSG:3857)Region extraction (/data/region.tif?left=v&right=v&top=v&bottom=v)Image thumbnails (/data/thumbnail.png)Individual pixels (/data/pixel?left=v&top=v)Band histograms (/data/histogram)Support for any storage backend:Supports Django'sFileFieldSupportsS3FileFieldCustomizable method for handling data access (get_pathoverride)Supports GDAL'sVirtual File Systemfors3://,ftp://, etc. URLsMiscellaneous:Admin interface widget for viewing image tiles.Cachingimage tiles and thumbnails are cached to prevent recreating these data on multiple requestsutilizes theDjango cache framework. Specify a named cache to use with theLARGE_IMAGE_CACHE_NAMEsetting.Easily extensible SSR templates for tile viewing with CesiumJS and GeoJSOpenAPI specificationOpenAPI DocumentationTiles Endpoint⬇️ InstallationOut of the box,django-large-imageonly depends on the corelarge-imagemodule, but you will need alarge-image-source-*module in order for this
to work. Most of our users probably want to work with geospatial images so we
will focus on thelarge-image-source-gdaland ``large-image-source-rasteriocases, but it is worth noting thatlarge-image` has source modules for a wide
variety of image formats (e.g., medical image formats for microscopy).Seelarge-image's
installation instructions for more details.🎡 pipRasteriopipinstall\django-large-image\'large-image[rasterio,pil]>=1.22'GDALTip:installing GDAL is notoriously difficult, so at Kitware we provide
pre-built Python wheels with the GDAL binary bundled for easily installation in
productionlinuxenvironments. To install our GDAL wheel, use:pip install --find-links https://girder.github.io/large_image_wheels GDALpipinstall\--find-linkshttps://girder.github.io/large_image_wheels\django-large-image\'large-image[gdal,pil]>=1.16.2'🐍 CondaOr install withconda:condainstall-cconda-forgedjango-large-imagelarge-image-source-rasteriocondainstall-cconda-forgedjango-large-imagelarge-image-source-gdal🚀 UsageSimply install the app and mixin one of the mixing classes to your
existingdjango-rest-frameworkviewset.# settings.pyINSTALLED_APPS=[...,'django_large_image',]The following are the provided mixin classes and their use case:LargeImageMixin: for use with a standard, non-detailViewSet. Users must implementget_path()LargeImageDetailMixin: for use with a detail viewset likeGenericViewSet. Users must implementget_path()LargeImageFileDetailMixin: (most commonly used) for use with a detail viewset likeGenericViewSetwhere the associated model has aFileFieldstoring the image data.LargeImageVSIFileDetailMixin: (geospatial) for use with a detail viewset likeGenericViewSetwhere the associated model has aFileFieldstoring the image data that is intended to be read with GDAL/rasterio. This will access the data over GDAL's Virtual File System interface (a VSI path).Most users will want to useLargeImageFileDetailMixinand so the following
example demonstrate how to use it:Specify theFILE_FIELD_NAMEas the string name of theFileFieldin which
your image data are saved on the associated model.# viewsets.pyfromrest_frameworkimportviewsetsfromdjango_large_image.restimportLargeImageFileDetailMixinclassMyModelViewSet(viewsets.GenericViewSet,LargeImageFileDetailMixin):...# configuration for your model's viewsetFILE_FIELD_NAME='field_name'# urls.pyfromdjango.urlsimportinclude,pathfromrest_framework.routersimportSimpleRouterfrommyapp.viewsetsimportMyModelViewSetrouter=SimpleRouter(trailing_slash=False)router.register(r'api/my-model',MyModelViewSet)urlpatterns=[# Additional, standalone URLs from django-large-imagepath('',include('django_large_image.urls')),]+router.urlsAnd that's it!📝 Example CodeTo use the mixin classes provided here, adddjango_large_imageto theINSTALLED_APPSof your Django project, then create a model, serializer,
and viewset in your Django project like so:# models.pyfromdjango.dbimportmodelsfromrest_frameworkimportserializersclassImageFile(models.Model):name=models.TextField()file=models.FileField()classImageFileSerializer(serializers.ModelSerializer):classMeta:model=ImageFilefields='__all__'# admin.pyfromdjango.contribimportadminfromexample.core.modelsimportImageFile@admin.register(ImageFile)classImageFileAdmin(admin.ModelAdmin):list_display=('pk','name')Then create the viewset, mixing in thedjango-large-imageviewset class:# viewsets.pyfromexample.coreimportmodelsfromrest_frameworkimportmixins,viewsetsfromdjango_large_image.restimportLargeImageFileDetailMixinclassImageFileDetailViewSet(mixins.ListModelMixin,viewsets.GenericViewSet,LargeImageFileDetailMixin,):queryset=models.ImageFile.objects.all()serializer_class=models.ImageFileSerializer# for `django-large-image`: the name of the image FileField on your modelFILE_FIELD_NAME='file'Then register the URLs:# urls.pyfromdjango.urlsimportinclude,pathfromexample.core.viewsetsimportImageFileDetailViewSetfromrest_framework.routersimportSimpleRouterrouter=SimpleRouter(trailing_slash=False)router.register(r'api/image-file',ImageFileDetailViewSet)urlpatterns=[# Additional, standalone URLs from django-large-imagepath('',include('django_large_image.urls')),]+router.urls(Optional) You can also use an admin widget for your model:<!-- templates/admin/myapp/imagefile/change_form.html -->{% extends "admin/change_form.html" %}

{% block after_field_sets %}<script>varbaseEndpoint='api/image-file';</script>{% include 'admin/django_large_image/_include/geojs.html' %}

{% endblock %}Please note the example Django project in theproject/directory of this
repository that shows how to usedjango-large-imagein agirder-4project.🛠️ CustomizationThe mixin classes are modularly designed and able to be subclassed
for your project's needs. While the providedLargeImageFileDetailMixinhandlesFileField-interfaces, you can easily extend its base class,LargeImageDetailMixin, to handle any mechanism of data storage in your
detail-oriented viewset.In the following example, we demonstrate how to use GDAL compatible VSI paths
from a model that storess3://orhttps://URLs.# models.pyfromdjango.dbimportmodelsfromrest_frameworkimportserializersclassURLImageFile(models.Model):name=models.TextField()url=models.TextField()classURLImageFileSerializer(serializers.ModelSerializer):classMeta:model=URLImageFilefields='__all__'# admin.pyfromdjango.contribimportadminfromexample.core.modelsimportURLImageFile@admin.register(URLImageFile)classURLImageFileAdmin(admin.ModelAdmin):list_display=('pk','name')# viewsets.pyfromexample.coreimportmodelsfromrest_frameworkimportmixins,viewsetsfromdjango_large_image.restimportLargeImageDetailMixinfromdjango_large_image.utilitiesimportmake_vsiclassURLLargeImageMixin(LargeImageDetailMixin):defget_path(self,request,pk=None):object=self.get_object()returnmake_vsi(object.url)classURLImageFileDetailViewSet(mixins.ListModelMixin,viewsets.GenericViewSet,URLLargeImageMixin,):queryset=models.URLImageFile.objects.all()serializer_class=models.URLImageFileSerializerHere is a good test image:https://oin-hotosm.s3.amazonaws.com/59c66c5223c8440011d7b1e4/0/7ad397c0-bba2-4f98-a08a-931ec3a6e943.tif🥸 Non-Detail ViewSetsTheLargeImageMixinprovides a mixin interface for non-detail viewsets (no
associated model or primary key required). This can be particularly useful if
your viewset has custom logic to retrieve the desired data.For example, you may want a viewset that gets the data path as a URL embedded
in the request's query parameters. To do this, you can make a standard ViewSet
with theLargeImageMixinlike so:# viewsets.pyfromrest_frameworkimportviewsetsfromrest_framework.exceptionsimportValidationErrorfromdjango_large_image.restimportLargeImageMixinfromdjango_large_image.utilitiesimportmake_vsiclassURLLargeImageViewSet(viewsets.ViewSet,LargeImageMixin):defget_path(self,request,pk=None):try:url=request.query_params.get('url')exceptKeyError:raiseValidationError('url must be defined as a query parameter.')returnmake_vsi(url)🪄 Stylingdjango-large-image's dynamic tile serving supports band styling and making
composite images from multiple frames and/or bands of your images. This means
that you can easily create a false color image from multispectral imagery.django-large-imagehas two styling modes:A simple interface to colormap a single channel using multiple query parameters. These are the documented OpenAPI query parameters.View a single band with a Matplotlib colormap:varthumbnailUrl=`http://localhost:8000/api/image-file/${imageId}/data/thumbnail.png?band=3&palette=viridis&min=50&max=250`;A complex specification for styling across frames and bands to create composite images using aJSON specification defined bylarge-image.Create a false color image from multiple bands in the source image:// See https://girder.github.io/large_image/tilesource_options.html#stylevarstyle={bands:[{band:5,palette:['#000','#f00']},// red{band:3,palette:['#000','#0f0']},// green{band:2,palette:['#000','#00f']}// blue]};varstyleEncoded=encodeURIComponent(JSON.stringify(style))varthumbnailUrl=`http://localhost:8000/api/image-file/${imageId}/data/thumbnail.png?style=${styleEncoded}`;☁️ Converting Images to Pyramidal Tiffs (COGs)Installlarge_image_converterand run the following:importlarge_image_converterlarge_image_converter.convert(input_path,output_path)It's that easy! The default parameters for that function will convert
geospatial rasters to Cloud Optimized GeoTiffs (COGs) and non-geospatial images
to a pyramidal tiff format.It's quite common to have a celery task that converts an image from a
model in your application. Here is a starting point:importosfromexample.coreimportmodelsfromceleryimportshared_taskimportlarge_image_converter# requires large-image-source-gdal@shared_taskdeftask_convert_cog(my_model_pk):image_file=models.ImageFile.objects.get(pk=my_model_pk)input_path=image_file.file.name# TODO: get full path to file on diskwithtempfile.TemporaryDirectory()astmpdir:output_path=os.path.join(tmpdir,'converted.tiff')large_image_converter.convert(input_path,output_path)# Do something with converted tiff file at `output_path`...If using therasterio-based source module, we recommend usingrio-cogeooverlarge_image_converter.Using with django-rasterdjango-rasteris a popular
choice for storing geospatial raster data in Django.django-large-imageworks
well withdjango-rasterto provide additional endpoints for dynamic tile
serving and more.Please take a look at the demo project here:https://github.com/ResonantGeoData/django-raster-demoand raise any questions about usage withdjango-rasterthere.Demo AppThere is a vanilla Django project in thedemo/directory and this app
is published as a standalone Docker image that anyone can try out:dockerrun-it-p8000:8000-vdli_demo_data:/opt/django-project/dataghcr.io/girder/django-large-image-demo:latest