feat: autorotate

master
Tomas 5 years ago
parent 0ae3ac43ce
commit 26b22e9a6f

@ -154,6 +154,11 @@ func (i *Image) Rotate(a Angle) ([]byte, error) {
return i.Process(options)
}
// AutoRotate automatically rotates the image with no additional transformation based on the EXIF oritentation metadata, if available.
func (i *Image) AutoRotate() ([]byte, error) {
return i.Process(Options{autoRotateOnly: true})
}
// Flip flips the image about the vertical Y axis.
func (i *Image) Flip() ([]byte, error) {
options := Options{Flip: true}

@ -345,6 +345,38 @@ func TestImageRotate(t *testing.T) {
Write("testdata/test_image_rotate_out.jpg", buf)
}
func TestImageAutoRotate(t *testing.T) {
tests := []struct {
file string
orientation int
}{
{"exif/Landscape_1.jpg", 1},
{"exif/Landscape_2.jpg", 2},
{"exif/Landscape_3.jpg", 1},
{"exif/Landscape_4.jpg", 4},
{"exif/Landscape_5.jpg", 5},
{"exif/Landscape_6.jpg", 1},
{"exif/Landscape_7.jpg", 7},
}
for index, test := range tests {
img := initImage(test.file)
buf, err := img.AutoRotate()
if err != nil {
t.Errorf("Cannot process the image: %#v", err)
}
Write(fmt.Sprintf("testdata/test_autorotate_%d_out.jpg", index), buf)
meta, err := img.Metadata()
if err != nil {
t.Errorf("Cannot read image metadata: %#v", err)
}
if meta.Orientation != test.orientation {
t.Errorf("Invalid image orientation for %s: %d != %d", test.file, meta.Orientation, test.orientation)
}
}
}
func TestImageConvert(t *testing.T) {
buf, err := initImage("test.jpg").Convert(PNG)
if err != nil {

@ -25,6 +25,7 @@ type ImageMetadata struct {
EXIF EXIF
}
// EXIF image metadata
type EXIF struct {
Make string
Model string

@ -8,7 +8,7 @@ import "C"
const (
// Quality defines the default JPEG quality to be used.
Quality = 80
Quality = 75
// MaxSize defines the maximum pixels width or height supported.
MaxSize = 16383
)
@ -226,4 +226,7 @@ type Options struct {
OutputICC string
InputICC string
Palette bool
// private fields
autoRotateOnly bool
}

@ -30,10 +30,20 @@ func resizer(buf []byte, o Options) ([]byte, error) {
// Clone and define default options
o = applyDefaults(o, imageType)
// Ensure supported type
if !IsTypeSupported(o.Type) {
return nil, errors.New("Unsupported image output type")
}
// Autorate only
if o.autoRotateOnly {
image, err = vipsAutoRotate(image)
if err != nil {
return nil, err
}
return saveImage(image, o)
}
// Auto rotate image based on EXIF orientation header
image, rotated, err := rotateAndFlipImage(image, o)
if err != nil {
@ -375,7 +385,6 @@ func watermarkImageWithText(image *C.VipsImage, w Watermark) (*C.VipsImage, erro
}
func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.VipsImage, error) {
if len(w.Buf) == 0 {
return image, nil
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 104 KiB

@ -272,6 +272,18 @@ func vipsRotate(image *C.VipsImage, angle Angle) (*C.VipsImage, error) {
return out, nil
}
func vipsAutoRotate(image *C.VipsImage) (*C.VipsImage, error) {
var out *C.VipsImage
defer C.g_object_unref(C.gpointer(image))
err := C.vips_autorot_bridge(image, &out)
if err != 0 {
return nil, catchVipsError()
}
return out, nil
}
func vipsTransformICC(image *C.VipsImage, inputICC string, outputICC string) (*C.VipsImage, error) {
var out *C.VipsImage
defer C.g_object_unref(C.gpointer(image))

@ -222,6 +222,11 @@ vips_rotate_bridge(VipsImage *in, VipsImage **out, int angle) {
}
}
int
vips_autorot_bridge(VipsImage *in, VipsImage **out) {
return vips_autorot(in, out, NULL);
}
const char *
vips_exif_tag(VipsImage *image, const char *tag) {
const char *exif;

@ -82,6 +82,42 @@ func TestVipsRotate(t *testing.T) {
}
}
func TestVipsAutoRotate(t *testing.T) {
files := []struct {
name string
orientation int
}{
{"test.jpg", 0},
{"test_exif.jpg", 0},
{"exif/Landscape_1.jpg", 0},
{"exif/Landscape_2.jpg", 2},
{"exif/Landscape_3.jpg", 0},
{"exif/Landscape_4.jpg", 4},
{"exif/Landscape_5.jpg", 5},
{"exif/Landscape_6.jpg", 0},
{"exif/Landscape_7.jpg", 7},
}
for _, file := range files {
image, _, _ := vipsRead(readImage(file.name))
newImg, err := vipsAutoRotate(image)
if err != nil {
t.Fatal("Cannot auto rotate the image")
}
orientation := vipsExifOrientation(newImg)
if orientation != file.orientation {
t.Fatalf("Invalid image orientation: %d != %d", orientation, file.orientation)
}
buf, _ := vipsSave(newImg, vipsSaveOptions{Quality: 95})
if len(buf) == 0 {
t.Fatal("Empty image")
}
}
}
func TestVipsZoom(t *testing.T) {
image, _, _ := vipsRead(readImage("test.jpg"))

Loading…
Cancel
Save