disintegration / imaging

Imaging is a simple image processing package for Go
MIT License
5.23k stars 435 forks source link

Rotate90() makes image to loose quality #91

Closed crosslogic closed 5 years ago

crosslogic commented 5 years ago

I'm working with JPEG file. I've noticed that every time I rotate the image, there's an incremental loss in the image quality.

Original: image

20 times rotated: image

Is there a way to avoid this?

disintegration commented 5 years ago

JPEG is a lossy image file format so each time the image is saved the quality decreases.

Most of the times you need to process an image once: load the image from file -> do all the image manipulations -> save the image to file.

If you really need to open and save the same image multiple times, consider using a lossless image format, e.g. PNG.

crosslogic commented 5 years ago

I've tried converting the intial image to PNG,

    img, err := jpeg.Decode(bytes.NewReader(imageBytes))
    if err != nil {
        return nil, errors.Wrap(err, "no se pudo leer jpeg")
    }

    buf := new(bytes.Buffer)
    if err := png.Encode(buf, img); err != nil {
        return nil, errors.Wrap(err, "no se pudo leer png")
    }

    png := buf.Bytes()

Size increased 7 fold, but got the same quality loss after the processing... Am I missing something?

disintegration commented 5 years ago

I want to help, but I need more information. The code you provided decodes the jpeg image and encodes it as png. It does not do any image manipulations. Normally it should not decrease the image quality.

Could you please provide a complete code example and the input image you use, so I can reproduce your results?

For example, here's the code that opens, rotates and saves the same png image 20 times. I ran it several times and I don't see any quality loss.

package main

import (
    "log"

    "github.com/disintegration/imaging"
)

func main() {
    for i := 0; i < 20; i++ {
        img, err := imaging.Open("test.png")
        if err != nil {
            log.Fatal(err)
        }

        img = imaging.Rotate90(img)

        err = imaging.Save(img, "test.png")
        if err != nil {
            log.Fatal(err)
        }
    }
}

before: orig

after: test

paulm17 commented 5 years ago

@disintegration

I have a rotation requirement as well. I have noticed degradation when rotating JPG images.

Using your code. I rotated 2000 times here's my result.

Original: Original

Rotated 2000 times: Original

Seems fine for png though.

My use case is for a DAM. I'd rather not have to convert files from jpg to png and back again just to rotate.

Any ideas?

Thanks


Further update:

Tested with ImageMagick and the result is the same with jpg files. png absolutely no problem

I thought it was an issue until I did some googling:

https://stackoverflow.com/questions/543402/can-a-jpeg-compressed-image-be-rotated-without-a-loss-in-quality

Came across jpegtran and did the same test with 2000 rotations:

http://manpages.ubuntu.com/manpages/trusty/man1/jpegtran.1.html

Had no problem with the results.

Going to use it via the command line instead.

disintegration commented 5 years ago

@paulm17

Thank you for the information! Looks like jpegtran is a great tool that can be used to apply simple transformations to JPEG files directly in a lossless way.

Another possible solution is to set the EXIF orientation tag in a JPEG file. It specifies the transformation needed to display the image correctly. It also requires some kind of tool or library that allows to change EXIF metadata.

crosslogic commented 5 years ago

I had a bug in my code... I was storing it as JPEG. It works great with PNG!

disintegration commented 5 years ago

Glad to hear that!