Giter VIP home page Giter VIP logo

bimg's Introduction

bimg GoDoc Coverage Status License

Small Go package for fast high-level image processing using libvips via C bindings, providing a simple programmatic API.

bimg was designed to be a small and efficient library supporting common image operations such as crop, resize, rotate, zoom or watermark. It can read JPEG, PNG, WEBP natively, and optionally TIFF, PDF, GIF and SVG formats if [email protected]+ is compiled with proper library bindings. Lastly AVIF is supported as of [email protected]+. For AVIF support libheif needs to be compiled with an applicable AVIF en-/decoder.

bimg is able to output images as JPEG, PNG and WEBP formats, including transparent conversion across them.

bimg uses internally libvips, a powerful library written in C for image processing which requires a low memory footprint and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native image package, and in some cases it's even 8x faster processing JPEG images.

If you're looking for an HTTP based image processing solution, see imaginary.

bimg was heavily inspired in sharp, its homologous package built for node.js. bimg is used in production environments processing thousands of images per day.

v1 notice: bimg introduces some minor breaking changes in v1 release. If you're using gopkg.in, you can still rely in the v0 without worrying about API breaking changes.

Contents

Supported image operations

  • Resize
  • Enlarge
  • Crop (including smart crop support, libvips 8.5+)
  • Rotate (with auto-rotate based on EXIF orientation)
  • Flip (with auto-flip based on EXIF metadata)
  • Flop
  • Zoom
  • Thumbnail
  • Extract area
  • Watermark (using text or image)
  • Gaussian blur effect
  • Custom output color space (RGB, grayscale...)
  • Format conversion (with additional quality/compression settings)
  • EXIF metadata (size, alpha channel, profile, orientation...)
  • Trim (libvips 8.6+)

Prerequisites

  • libvips 8.3+ (8.8+ recommended)
  • C compatible compiler such as gcc 4.6+ or clang 3.0+
  • Go 1.3+

Note:

  • libvips v8.3+ is required for GIF, PDF and SVG support.
  • libvips v8.9+ is required for AVIF support. libheif compiled with a AVIF en-/decoder also needs to be present.

Installation

go get -u github.com/h2non/bimg

libvips

Follow libvips installation instructions:

https://libvips.github.io/libvips/install.html

Installation script

Note: install script is officially deprecated, it might not work as expected. We recommend following libvips install instructions.

Run the following script as sudo (supports OSX, Debian/Ubuntu, Redhat, Fedora, Amazon Linux):

curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -

If you want to take the advantage of OpenSlide, simply add --with-openslide to enable it:

curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -s --with-openslide

The install script requires curl and pkg-config.

Performance

libvips is probably the fastest open source solution for image processing. Here you can see some performance test comparisons for multiple scenarios:

Benchmark

Tested using Go 1.5.1 and libvips-7.42.3 in OSX i7 2.7Ghz

BenchmarkRotateJpeg-8     	      20	  64686945 ns/op
BenchmarkResizeLargeJpeg-8	      20	  63390416 ns/op
BenchmarkResizePng-8      	     100	  18147294 ns/op
BenchmarkResizeWebP-8     	     100	  20836741 ns/op
BenchmarkConvertToJpeg-8  	     100	  12831812 ns/op
BenchmarkConvertToPng-8   	      10	 128901422 ns/op
BenchmarkConvertToWebp-8  	      10	 204027990 ns/op
BenchmarkCropJpeg-8       	      30	  59068572 ns/op
BenchmarkCropPng-8        	      10	 117303259 ns/op
BenchmarkCropWebP-8       	      10	 107060659 ns/op
BenchmarkExtractJpeg-8    	      50	  30708919 ns/op
BenchmarkExtractPng-8     	    3000	    595546 ns/op
BenchmarkExtractWebp-8    	    3000	    386379 ns/op
BenchmarkZoomJpeg-8       	      10	 160005424 ns/op
BenchmarkZoomPng-8        	      30	  44561047 ns/op
BenchmarkZoomWebp-8       	      10	 126732678 ns/op
BenchmarkWatermarkJpeg-8  	      20	  79006133 ns/op
BenchmarkWatermarPng-8    	     200	   8197291 ns/op
BenchmarkWatermarWebp-8   	      30	  49360369 ns/op

Examples

import (
  "fmt"
  "os"
  "github.com/h2non/bimg"
)

Resize

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

newImage, err := bimg.NewImage(buffer).Resize(800, 600)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

size, err := bimg.NewImage(newImage).Size()
if size.Width == 800 && size.Height == 600 {
  fmt.Println("The image size is valid")
}

bimg.Write("new.jpg", newImage)

Rotate

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

newImage, err := bimg.NewImage(buffer).Rotate(90)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

bimg.Write("new.jpg", newImage)

Convert

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

newImage, err := bimg.NewImage(buffer).Convert(bimg.PNG)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

if bimg.NewImage(newImage).Type() == "png" {
  fmt.Fprintln(os.Stderr, "The image was converted into png")
}

Force resize

Force resize operation without preserving the aspect ratio:

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

newImage, err := bimg.NewImage(buffer).ForceResize(1000, 500)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

size := bimg.Size(newImage)
if size.Width != 1000 || size.Height != 500 {
  fmt.Fprintln(os.Stderr, "Incorrect image size")
}

Custom colour space (black & white)

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

newImage, err := bimg.NewImage(buffer).Colourspace(bimg.INTERPRETATION_B_W)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

colourSpace, _ := bimg.ImageInterpretation(newImage)
if colourSpace != bimg.INTERPRETATION_B_W {
  fmt.Fprintln(os.Stderr, "Invalid colour space")
}

Custom options

See Options struct to discover all the available fields

options := bimg.Options{
  Width:        800,
  Height:       600,
  Crop:         true,
  Quality:      95,
  Rotate:       180,
  Interlace:    true,
}

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

newImage, err := bimg.NewImage(buffer).Process(options)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

bimg.Write("new.jpg", newImage)

Watermark

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

watermark := bimg.Watermark{
  Text:       "Chuck Norris (c) 2315",
  Opacity:    0.25,
  Width:      200,
  DPI:        100,
  Margin:     150,
  Font:       "sans bold 12",
  Background: bimg.Color{255, 255, 255},
}

newImage, err := bimg.NewImage(buffer).Watermark(watermark)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

bimg.Write("new.jpg", newImage)

Fluent interface

buffer, err := bimg.Read("image.jpg")
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

image := bimg.NewImage(buffer)

// first crop image
_, err := image.CropByWidth(300)
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

// then flip it
newImage, err := image.Flip()
if err != nil {
  fmt.Fprintln(os.Stderr, err)
}

// save the cropped and flipped image
bimg.Write("new.jpg", newImage)

Debugging

Run the process passing the DEBUG environment variable

DEBUG=bimg ./app

Enable libvips traces (note that a lot of data will be written in stdout):

VIPS_TRACE=1 ./app

You can also dump a core on failure, as John Cuppit said:

g_log_set_always_fatal(
                G_LOG_FLAG_RECURSION |
                G_LOG_FLAG_FATAL |
                G_LOG_LEVEL_ERROR |
                G_LOG_LEVEL_CRITICAL |
                G_LOG_LEVEL_WARNING );

Or set the G_DEBUG environment variable:

export G_DEBUG=fatal-warnings,fatal-criticals

API

See godoc reference for detailed API documentation.

Authors

Credits

People who recurrently contributed to improve bimg in some way.

Thank you!

License

MIT - Tomas Aparicio

views

bimg's People

Contributors

ajdevries avatar bfitzsimmons avatar chonthu avatar danpersa avatar der-eismann avatar dynom avatar evanoberholster avatar golint-fixer avatar greut avatar h2non avatar henry-blip avatar igsr5 avatar jayme-github avatar jeremysf avatar kirilldanshin avatar kyfk avatar larrabee avatar larsfronius avatar mikepulaski avatar momo13 avatar mtribes-sdk-bot avatar nvartolomei avatar praveensanap avatar quentindanjou avatar skidder avatar totallyunknown avatar vaibsharma avatar vansante avatar zllak avatar zloydyadka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bimg's Issues

Memory leak?

Hi. The following is a simplified version of an HTTP server that resizes and serves an image off the filesystem.

Opening the same image in the browser several times (on localhost:8080), I can see the process memory going up. The more times I hit the server, the more the memory goes up, and stays there. It doesn't seem to go back down.

package main

import (
    "fmt"
    "html"
    "io/ioutil"
    "log"
    "net/http"

    bimg "gopkg.in/h2non/bimg.v0"
)

func main() {
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        bytes, err := ioutil.ReadFile("./fixtures/29/original/111900-_MG_2031.JPG")
        if err != nil {
            fmt.Fprintf(rw, "Err, %q", html.EscapeString(r.URL.Path))
            return
        }

        bimgImg := bimg.NewImage(bytes)

        var opts bimg.Options
        opts.Interpolator = bimg.BILINEAR
        opts.Width = 200
        opts.Height = 300
        opts.Enlarge = true

        newImage, err := bimgImg.Process(opts)
        if err != nil {
            fmt.Fprintf(rw, "Bimg Err, %q", html.EscapeString(r.URL.Path))
            return
        }
        rw.Write(newImage)
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}

This is happening in my MacBook Pro and also an Ubuntu machine. I tried with Go 1.4.2 and 1.5.1. Similar results.

screenshot 2015-09-29 21 16 51

screenshot 2015-09-29 21 17 11

screenshot 2015-09-29 21 16 26

This last one is after hammering the server with ab -n 1000 -c 3 http://127.0.0.1:8080/. It stays there and doesn't go back down.

screenshot 2015-09-29 21 53 42

For good measure, I tried the following script that just reads the file and serves the bytes, without Bimg. That version tops at ~6.7MB and stops there, so no apparent leak.

package main

import (
    "fmt"
    "html"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        bytes, err := ioutil.ReadFile("./fixtures/29/original/111900-_MG_2031.JPG")
        if err != nil {
            fmt.Fprintf(rw, "Err, %q", html.EscapeString(r.URL.Path))
            return
        }

        rw.Write(bytes)
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}

// top: 6.7MB. No leak.

Have you encountered this? Or maybe I'm missing something obvious in the way I'm using Bimg?

Regards.

GIF support

Currently GIF images are detected with the magick code but fails the IsTypeSupported check in type.go package. Is there any blocker to support GIF (via libvips)?

I know you can process GIFs using libvips but only if you configure it with libMagick support, maybe it's the culprit.

Greyscale support

Is there an API to convert images to greyscale currently? Looking at the code I can see that this line would allow me to pass the necessary options down to Vips, but I'm not sure how to go about it.

Regards.

Openslide dependency

When installing VIPS using the provided gist, a dependency on openslide is introduced, and therefore a requirement to run Homebrew as root is also introduced. This is officially "not supported" by Homebrew, and kills the script if you have installed Homebrew as required.

The failure:

$ curl -s https://bitbucket.org/api/2.0/snippets/bfitzsimmons/AaAk/52663c0a731eebeac46bd60bb2d9b1e7916417c2/files/vips_install | sudo bash -
Could not find libvips using a PKG_CONFIG_PATH of '/usr/lib/pkgconfig:/usr/local/Library/ENV/pkgconfig/10.10::/usr/local/lib/pkgconfig:/usr/lib/pkgconfig'
Detected Mac OS
Installing libvips via homebrew
Error: Cowardly refusing to `sudo brew install`
You can use brew with sudo, but only if the brew executable is owned by root.
However, this is both not recommended and completely unsupported so do so at
your own risk.

The reason for the failure (note the password prompt):

$ brew install Caskroom/cask/xquartz
==> Tapping Caskroom/cask
Cloning into '/usr/local/Library/Taps/caskroom/homebrew-cask'...
...
==> Checking out tag v0.54.0
🍺  /usr/local/Cellar/brew-cask/0.54.0: 2634 files, 10M, built in 4 seconds
==> brew cask install Caskroom/cask/xquartz
==> We need to make Caskroom for the first time at /opt/homebrew-cask/Caskroom
==> We'll set permissions properly so we won't need sudo in the future
Password:
...
🍺  xquartz staged at '/opt/homebrew-cask/Caskroom/xquartz/2.7.7' (64M)

I know that the gist is coming from the sharp project, but if bimg does not require openslide, should a new gist be created that avoids the dependency? Perhaps a modification of the current one?

Fuzz testing

  • Test corrupt images (partially done)
  • Invalid EXIF headers
  • Image operations with higher width/height than the original one

Fluent API design

options := bimg.Options{
  Type: "webp",
  Crop: true,
  Width: 100,
  Height: 100,
}
image := bimg.NewImage(buffer)
buf, err := image.Resize(options)
if err != nil {
   fmt.Printf("Error: %s", err)
}

Unsupported image format

I'm using bimg in a function. Whole project is running as a service in a background. My usage is pretty simple.

After "while" usually anything between 1-2 days, and after processing hips of images (hard to say, but we are talking about 1000-2000 images) .Process(options) function crashes with "Unsupported image format".

After restart everything is back to normal. I've checked source images - and they are ok, and after restart same image which wasn't recognised correctly is ok. It's super hard to debug because I can't reproduce problem without leaving service for 1-2 days..

Any clues ? I'm doing something wrong here ?


/**
 * resize image action
 * 
 * @param inBuf []byte image to resize
 * @param w, h int width and height
 * @param c
 */
func Resize(inBuf []byte, w int, h int, c bool, t, quality int) []byte {

    options := bimg.Options{
        Width : w,
        Height : h,
        Crop : c,
        //Embed : true,
        Interpolator : bimg.BICUBIC,
        Gravity: bimg.CENTRE,
        Quality:      quality,
    }

    // change image type
    if t > 0 {
        options.Type = bimg.ImageType(t)
    }

    image := bimg.NewImage(inBuf)

    newImage, err := image.Process(options)


    if err != nil {
        fmt.Println("failed to process image")
        log.Printf("options: %v", options)

        //bimg.VipsDebugInfo()
        fmt.Fprintln(os.Stderr, err)
        LastError = err.Error()

    }

    return newImage
}

Slow webp convert

Hello,

I have very slow convert to webp (all other results are +- the same, but Convert to webp is 1000 times slower) - it was checked on your benchmarks

Please, help to find problem...

Strange behaviour with Resize

Hi,

I'm trying to make sense of the Resize function. From the resize_test.go file, I've adapted the following.

  • TestResize doesn't affect the source file and the output is a copy of it
  • TestResize2 (bad naming) does work and produces the smaller file.

I don't get why in affineTransform (resize.go:256), the tranformation just made is rejected.

func TestResize(t *testing.T) {
    options := Options{Width: 800, Height: 600}
    buf, _ := Read("fixtures/test.jpg")

    newImg, err := Resize(buf, options)
    if err != nil {
        t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
    }

    size, _ := NewImage(newImg).Size()
    if size.Width > options.Width || size.Height > options.Height {
        t.Errorf("New image bigger than the options. %#v", size)
    }
}

func TestResize2(t *testing.T) {
    options := Options{Width: 105, Height: 66}
    buf, _ := Read("fixtures/test.jpg")

    newImg, err := Resize(buf, options)
    if err != nil {
        t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
    }

    size, _ := NewImage(newImg).Size()
    if size.Width > options.Width || size.Height > options.Height {
        t.Errorf("New image bigger than the options. %#v", size)
    }
}

Cheers!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.