lovell / sharp

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images. Uses the libvips library.
https://sharp.pixelplumbing.com
Apache License 2.0
28.9k stars 1.29k forks source link

Enhancement: add support for libvips compiled with support for Radiance #3544

Open sb55555 opened 1 year ago

sb55555 commented 1 year ago

System: OS: Linux 5.15 Amazon Linux 2 CPU: (8) x64 Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz Memory: 7.44 GB / 15.53 GB Container: Yes Shell: 4.2.46 - /bin/bash Binaries: Node: 16.18.1 - /var/lang/bin/node npm: 8.19.2 - /var/lang/bin/npm npmPackages: sharp: ^0.31.3 => 0.31.3

What are the steps to reproduce?

index.js

'use strict';

const sharp = require('sharp');
const util = require('util');

const run = async () => {
  console.log('sharp.format', util.inspect(sharp.format, false, null, true));
  console.log('sharp.versions', util.inspect(sharp.versions, false, null, true));
  console.log('sharp.vendor', util.inspect(sharp.vendor, false, null, true));

  // perform the resize operation
  const buffer = await sharp('vestibule_4k.hdr')
    .resize(256, 256)
    .toBuffer();
};

run();

Run the below Dockerfile with docker build -t dev-lambda . --progress plain --no-cache

Dockerfile

FROM amazon/aws-lambda-nodejs:16
# FROM amazonlinux:2

ARG BUILD_DIR="/var/build"
ARG FUNCTION_DIR="/var/task"
ARG VERSION_VIPS=8.14.1

RUN \
  yum -q -y groupinstall "Development Tools"

RUN \
  yum -q -y install \
    nano \
    cmake3 \
    wget \
    gobject-introspection-devel \
    glib2-devel \
    expat-devel \
    orc-devel \
    lcms2-devel \
    libexif-devel \
    libgsf-devel \
    libjpeg-turbo-devel \
    libpng-devel \
    giflib-devel \
    libwebp-devel \
    fftw-devel \
    libtiff-devel \
    ImageMagick-devel \
  && ln -s /usr/bin/cmake3 /usr/bin/cmake

RUN \
  pip3 install \
    meson \
    ninja

# Install awscli
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -s
RUN unzip -q awscliv2.zip
RUN ./aws/install

# Create build directory
RUN mkdir -p ${BUILD_DIR}

RUN \
  cd ${BUILD_DIR} \
  && wget https://github.com/libvips/libvips/archive/refs/tags/v${VERSION_VIPS}.tar.gz \
  && tar xf v${VERSION_VIPS}.tar.gz

RUN \
  cd ${BUILD_DIR}/libvips-${VERSION_VIPS} \
  && meson setup build-dir \
      --buildtype=release \
      -Dexamples=false \
  && cd build-dir \
  && ninja \
  && ninja install \
  && ln -s /usr/local/lib64/libvips-cpp.so.42 /usr/lib64/libvips-cpp.so.42 \
  && ln -s /usr/local/lib64/libvips.so.42 /usr/lib64/libvips.so.42

# Create function directory
RUN mkdir -p ${FUNCTION_DIR}

# Copy handler function
COPY index.js ${FUNCTION_DIR}

RUN cd ${FUNCTION_DIR} && npm init -f -y \
     && PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig npm install --verbose --foreground-scripts sharp aws-sdk

CMD [ "index.handler" ]

What is the expected behaviour?

With a custom build of libvips that enables Rad (Radiance HDR), I expect sharp to be able to resize .hdr files.

Libvips installs says that Radiance HDR is enabled

13 12.96 enable RAD load/save : YES

#13 12.96 vips 8.14.1
#13 12.96 
#13 12.96   Build options
#13 12.96     enable debug                      : NO
#13 12.96     enable deprecated                 : YES
#13 12.96     enable modules                    : YES
#13 12.96     enable gtk-doc                    : NO
#13 12.96     enable doxygen                    : NO
#13 12.96     enable introspection              : YES
#13 12.96     enable examples                   : NO
#13 12.96     enable cplusplus                  : YES
#13 12.96     enable RAD load/save              : YES
#13 12.96     enable Analyze7 load/save         : YES
#13 12.96     enable PPM load/save              : YES
#13 12.96     enable GIF load                   : YES
#13 12.96 
#13 12.96   Optional external packages
#13 12.96     use fftw for FFTs                 : YES
#13 12.96     accelerate loops with ORC         : YES
#13 12.96     ICC profile support with lcms     : YES
#13 12.96     zlib                              : YES
#13 12.96     text rendering with pangocairo    : NO
#13 12.96     font file support with fontconfig : NO
#13 12.96     EXIF metadata support with libexif: YES
#13 14.19 
#13 14.19   External image format libraries
#13 14.19     JPEG load/save with libjpeg       : YES
#13 14.19     JXL load/save with libjxl         : NO (dynamic module: NO)
#13 14.19     JPEG2000 load/save with OpenJPEG  : NO
#13 14.19     PNG load/save with libspng        : NO
#13 14.19     PNG load/save with libpng         : YES
#13 14.19     selected quantisation package     : none
#13 14.19     TIFF load/save with libtiff       : YES
#13 14.19     image pyramid save with libgsf    : YES
#13 14.19     HEIC/AVIF load/save with libheif  : NO (dynamic module: NO)
#13 14.19     WebP load/save with libwebp       : NO
#13 14.19     PDF load with PDFium              : NO
#13 14.19     PDF load with poppler-glib        : NO (dynamic module: NO)
#13 14.19     SVG load with librsvg             : NO
#13 14.19     EXR load with OpenEXR             : NO
#13 14.19     OpenSlide load                    : NO (dynamic module: NO)
#13 14.19     Matlab load with libmatio         : NO
#13 14.19     NIfTI load/save with niftiio      : NO
#13 14.19     FITS load/save with cfitsio       : NO
#13 14.19     GIF save with cgif                : NO
#13 14.19     selected Magick package           : MagickCore (dynamic module: YES)
#13 14.19     Magick API version                : magick6
#13 14.19     Magick load                       : YES
#13 14.19     Magick save                       : YES
#13 14.19 
#13 14.19   User defined options
#13 14.19     buildtype                         : release
#13 14.19     examples                          : false
#13 14.19 

sharp build from source says that local libvips is found

#17 10.48 > sharp@0.31.3 install
#17 10.48 > (node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)
#17 10.48 
#17 10.72 sharp: Detected globally-installed libvips v8.14.1
#17 10.72 sharp: Building from source via node-gyp

Im able to resize a Radiance HDR with vips cmdline

docker run -it --entrypoint "/bin/bash" --rm dev-lambda
wget https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/4k/vestibule_4k.hdr
vips resize vestibule_4k.hdr vestibule_256.hdr 0.125

but sharp raises an error

node index.js 
bash-4.2# node index.js 
sharp.format {
  jpeg: {
    id: 'jpeg',
    input: {
      file: true,
      buffer: true,
      stream: true,
      fileSuffix: [ '.jpg', '.jpeg', '.jpe' ]
    },
    output: { file: true, buffer: true, stream: true, alias: [ 'jpe', 'jpg' ] }
  },
  png: {
    id: 'png',
    input: { file: true, buffer: true, stream: true, fileSuffix: [ '.png' ] },
    output: { file: true, buffer: true, stream: true }
  },
  webp: {
    id: 'webp',
    input: { file: false, buffer: false, stream: false },
    output: { file: false, buffer: false, stream: false }
  },
  tiff: {
    id: 'tiff',
    input: {
      file: true,
      buffer: true,
      stream: true,
      fileSuffix: [ '.tif', '.tiff' ]
    },
    output: { file: true, buffer: true, stream: true, alias: [ 'tif' ] }
  },
  magick: {
    id: 'magick',
    input: { file: true, buffer: true, stream: true },
    output: { file: true, buffer: true, stream: true }
  },
  openslide: {
    id: 'openslide',
    input: { file: false, buffer: false, stream: false },
    output: { file: false, buffer: false, stream: false }
  },
  dz: {
    id: 'dz',
    input: { file: false, buffer: false, stream: false },
    output: { file: true, buffer: true, stream: true }
  },
  ppm: {
    id: 'ppm',
    input: {
      file: true,
      buffer: false,
      stream: false,
      fileSuffix: [ '.pbm', '.pgm', '.ppm', '.pfm', '.pnm' ]
    },
    output: { file: true, buffer: false, stream: false }
  },
  fits: {
    id: 'fits',
    input: { file: false, buffer: false, stream: false },
    output: { file: false, buffer: false, stream: false }
  },
  gif: {
    id: 'gif',
    input: { file: true, buffer: true, stream: true, fileSuffix: [ '.gif' ] },
    output: { file: false, buffer: false, stream: false }
  },
  svg: {
    id: 'svg',
    input: { file: false, buffer: false, stream: false },
    output: { file: false, buffer: false, stream: false }
  },
  heif: {
    id: 'heif',
    input: { file: false, buffer: false, stream: false },
    output: {
      file: false,
      buffer: false,
      stream: false,
      alias: [ 'avif', 'heic' ]
    }
  },
  pdf: {
    id: 'pdf',
    input: { file: false, buffer: false, stream: false },
    output: { file: false, buffer: false, stream: false }
  },
  vips: {
    id: 'vips',
    input: {
      file: true,
      buffer: false,
      stream: false,
      fileSuffix: [ '.v', '.vips' ]
    },
    output: { file: true, buffer: false, stream: false }
  },
  jp2k: {
    id: 'jp2k',
    input: { file: false, buffer: false, stream: false },
    output: {
      file: false,
      buffer: false,
      stream: false,
      alias: [ 'j2c', 'j2k', 'jp2', 'jpx' ]
    }
  },
  jxl: {
    id: 'jxl',
    input: { file: false, buffer: false, stream: false },
    output: { file: false, buffer: false, stream: false }
  },
  raw: {
    id: 'raw',
    input: { file: false, buffer: true, stream: true },
    output: { file: false, buffer: true, stream: true }
  }
}
sharp.versions { vips: '8.14.1' }
sharp.vendor { current: 'linux-x64', installed: [] }
node:internal/process/promises:279
            triggerUncaughtException(err, true /* fromPromise */);
            ^

[Error: Input file contains unsupported image format]
lovell commented 1 year ago

I don't think the radload feature is supported at present so we'd need to add it.

Happy to accept a PR, if you're able.

The ImageTypeId function and loaderToType map are a couple of places we'd need to do this for Radiance input.

https://github.com/lovell/sharp/blob/844deaf48060d60eb05f3f61c567282e3611f12a/src/common.cc#L234

https://github.com/lovell/sharp/blob/844deaf48060d60eb05f3f61c567282e3611f12a/src/common.cc#L267

For Radiance output, we would need some custom saving logic; perhaps look at the jp2 code for guidance.