github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/images/images_test.go (about)

     1  /*
     2  Copyright 2012 The Camlistore Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package images
    18  
    19  import (
    20  	"image"
    21  	"image/jpeg"
    22  	"os"
    23  	"path/filepath"
    24  	"sort"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"camlistore.org/third_party/github.com/camlistore/goexif/exif"
    30  )
    31  
    32  const datadir = "testdata"
    33  
    34  func equals(im1, im2 image.Image) bool {
    35  	if !im1.Bounds().Eq(im2.Bounds()) {
    36  		return false
    37  	}
    38  	for y := 0; y < im1.Bounds().Dy(); y++ {
    39  		for x := 0; x < im1.Bounds().Dx(); x++ {
    40  			r1, g1, b1, a1 := im1.At(x, y).RGBA()
    41  			r2, g2, b2, a2 := im2.At(x, y).RGBA()
    42  			if r1 != r2 || g1 != g2 || b1 != b2 || a1 != a2 {
    43  				return false
    44  			}
    45  		}
    46  	}
    47  	return true
    48  }
    49  
    50  func straightFImage(t *testing.T) image.Image {
    51  	g, err := os.Open(filepath.Join(datadir, "f1.jpg"))
    52  	if err != nil {
    53  		t.Fatal(err)
    54  	}
    55  	defer g.Close()
    56  	straightF, err := jpeg.Decode(g)
    57  	if err != nil {
    58  		t.Fatal(err)
    59  	}
    60  	return straightF
    61  }
    62  
    63  func smallStraightFImage(t *testing.T) image.Image {
    64  	g, err := os.Open(filepath.Join(datadir, "f1-s.jpg"))
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  	defer g.Close()
    69  	straightF, err := jpeg.Decode(g)
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	return straightF
    74  }
    75  
    76  func sampleNames(t *testing.T) []string {
    77  	dir, err := os.Open(datadir)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  	defer dir.Close()
    82  	samples, err := dir.Readdirnames(-1)
    83  	if err != nil {
    84  		t.Fatal(err)
    85  	}
    86  	sort.Strings(samples)
    87  	return samples
    88  }
    89  
    90  // TestEXIFCorrection tests that the input files with EXIF metadata
    91  // are correctly automatically rotated/flipped when decoded.
    92  func TestEXIFCorrection(t *testing.T) {
    93  	samples := sampleNames(t)
    94  	straightF := straightFImage(t)
    95  	for _, v := range samples {
    96  		if !strings.Contains(v, "exif") || strings.HasSuffix(v, "-s.jpg") {
    97  			continue
    98  		}
    99  		name := filepath.Join(datadir, v)
   100  		t.Logf("correcting %s with EXIF Orientation", name)
   101  		f, err := os.Open(name)
   102  		if err != nil {
   103  			t.Fatal(err)
   104  		}
   105  		defer f.Close()
   106  		im, _, err := Decode(f, nil)
   107  		if err != nil {
   108  			t.Fatal(err)
   109  		}
   110  		if !equals(im, straightF) {
   111  			t.Fatalf("%v not properly corrected with exif", name)
   112  		}
   113  	}
   114  }
   115  
   116  // TestForcedCorrection tests that manually specifying the
   117  // rotation/flipping to be applied when decoding works as
   118  // expected.
   119  func TestForcedCorrection(t *testing.T) {
   120  	samples := sampleNames(t)
   121  	straightF := straightFImage(t)
   122  	for _, v := range samples {
   123  		if strings.HasSuffix(v, "-s.jpg") {
   124  			continue
   125  		}
   126  		name := filepath.Join(datadir, v)
   127  		t.Logf("forced correction of %s", name)
   128  		f, err := os.Open(name)
   129  		if err != nil {
   130  			t.Fatal(err)
   131  		}
   132  		defer f.Close()
   133  		num := name[10]
   134  		angle, flipMode := 0, 0
   135  		switch num {
   136  		case '1':
   137  			// nothing to do
   138  		case '2':
   139  			flipMode = 2
   140  		case '3':
   141  			angle = 180
   142  		case '4':
   143  			angle = 180
   144  			flipMode = 2
   145  		case '5':
   146  			angle = -90
   147  			flipMode = 2
   148  		case '6':
   149  			angle = -90
   150  		case '7':
   151  			angle = 90
   152  			flipMode = 2
   153  		case '8':
   154  			angle = 90
   155  		}
   156  		im, _, err := Decode(f, &DecodeOpts{Rotate: angle, Flip: FlipDirection(flipMode)})
   157  		if err != nil {
   158  			t.Fatal(err)
   159  		}
   160  		if !equals(im, straightF) {
   161  			t.Fatalf("%v not properly corrected", name)
   162  		}
   163  	}
   164  }
   165  
   166  // TestRescale verifies that rescaling an image, without
   167  // any rotation/flipping, produces the expected image.
   168  func TestRescale(t *testing.T) {
   169  	name := filepath.Join(datadir, "f1.jpg")
   170  	t.Logf("rescaling %s with half-width and half-height", name)
   171  	f, err := os.Open(name)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  	defer f.Close()
   176  	rescaledIm, _, err := Decode(f, &DecodeOpts{ScaleWidth: 0.5, ScaleHeight: 0.5})
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	smallIm := smallStraightFImage(t)
   182  
   183  	gotB, wantB := rescaledIm.Bounds(), smallIm.Bounds()
   184  	if !gotB.Eq(wantB) {
   185  		t.Errorf("(scale) %v bounds not equal, got %v want %v", name, gotB, wantB)
   186  	}
   187  	if !equals(rescaledIm, smallIm) {
   188  		t.Errorf("(scale) %v pixels not equal", name)
   189  	}
   190  
   191  	_, err = f.Seek(0, os.SEEK_SET)
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  
   196  	rescaledIm, _, err = Decode(f, &DecodeOpts{MaxWidth: 2000, MaxHeight: 40})
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	gotB = rescaledIm.Bounds()
   201  	if !gotB.Eq(wantB) {
   202  		t.Errorf("(max) %v bounds not equal, got %v want %v", name, gotB, wantB)
   203  	}
   204  	if !equals(rescaledIm, smallIm) {
   205  		t.Errorf("(max) %v pixels not equal", name)
   206  	}
   207  }
   208  
   209  // TestRescaleEXIF verifies that rescaling an image, followed
   210  // by the automatic EXIF correction (rotation/flipping),
   211  // produces the expected image. All the possible correction
   212  // modes are tested.
   213  func TestRescaleEXIF(t *testing.T) {
   214  	smallStraightF := smallStraightFImage(t)
   215  	samples := sampleNames(t)
   216  	for _, v := range samples {
   217  		if !strings.Contains(v, "exif") {
   218  			continue
   219  		}
   220  		name := filepath.Join(datadir, v)
   221  		t.Logf("rescaling %s with half-width and half-height", name)
   222  		f, err := os.Open(name)
   223  		if err != nil {
   224  			t.Fatal(err)
   225  		}
   226  		defer f.Close()
   227  		rescaledIm, _, err := Decode(f, &DecodeOpts{ScaleWidth: 0.5, ScaleHeight: 0.5})
   228  		if err != nil {
   229  			t.Fatal(err)
   230  		}
   231  
   232  		gotB, wantB := rescaledIm.Bounds(), smallStraightF.Bounds()
   233  		if !gotB.Eq(wantB) {
   234  			t.Errorf("(scale) %v bounds not equal, got %v want %v", name, gotB, wantB)
   235  		}
   236  		if !equals(rescaledIm, smallStraightF) {
   237  			t.Errorf("(scale) %v pixels not equal", name)
   238  		}
   239  
   240  		_, err = f.Seek(0, os.SEEK_SET)
   241  		if err != nil {
   242  			t.Fatal(err)
   243  		}
   244  		rescaledIm, _, err = Decode(f, &DecodeOpts{MaxWidth: 2000, MaxHeight: 40})
   245  		if err != nil {
   246  			t.Fatal(err)
   247  		}
   248  
   249  		gotB = rescaledIm.Bounds()
   250  		if !gotB.Eq(wantB) {
   251  			t.Errorf("(max) %v bounds not equal, got %v want %v", name, gotB, wantB)
   252  		}
   253  		if !equals(rescaledIm, smallStraightF) {
   254  			t.Errorf("(max) %v pixels not equal", name)
   255  		}
   256  	}
   257  }
   258  
   259  // TODO(mpl): move this test to the goexif lib if/when we contribute
   260  // back the DateTime stuff to upstream.
   261  func TestDateTime(t *testing.T) {
   262  	f, err := os.Open(filepath.Join(datadir, "f1-exif.jpg"))
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  	defer f.Close()
   267  	ex, err := exif.Decode(f)
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  	got, err := ex.DateTime()
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  	exifTimeLayout := "2006:01:02 15:04:05"
   276  	want, err := time.Parse(exifTimeLayout, "2012:11:04 05:42:02")
   277  	if err != nil {
   278  		t.Fatal(err)
   279  	}
   280  	if got != want {
   281  		t.Fatalf("Creation times differ; got %v, want: %v\n", got, want)
   282  	}
   283  }