Beispiel #1
0
//best effort set location from exif data.
func setLocationFromExif(l *core.LatLon, x *exif.Exif) {
	lat, err := x.Get(exif.GPSLatitude)
	if err != nil || lat.Format() != tiff.RatVal || lat.Count != 3 {
		return
	}
	lon, err := x.Get(exif.GPSLongitude)
	if err != nil || lon.Format() != tiff.RatVal || lon.Count != 3 {
		return
	}
	//we should have 3 rational values. and this lib panics on fail.
	//so we did the checks first.
	//OK it no longer panics, so we need to error check. so we add a helper
	rats := func(tag *tiff.Tag) []*big.Rat {
		r := make([]*big.Rat, 3)
		r[0], _ = tag.Rat(0)
		r[1], _ = tag.Rat(1)
		r[2], _ = tag.Rat(2)
		return r
	}

	l.Lat = toDecimalDegreesFromRat(rats(lat))
	l.Lon = toDecimalDegreesFromRat(rats(lon))
	//l is a pointer, so we have filled in the values now,
	// and do not need to return anything.
}
Beispiel #2
0
func tagTime(ex *exif.Exif, ts exif.FieldName) (t time.Time, err error) {
	t = time.Unix(0, 0)
	tg, err := ex.Get(ts)
	if err != nil {
		return
	}
	str_val, err := tg.StringVal()
	if err != nil {
		return
	}
	t, err = time.Parse("2006:01:02 15:04:05", str_val)
	return
}
Beispiel #3
0
// Returns pointer to struct GeoFields
// can easily be accessed E.Lat, E.Long etc
func GetGPS(E *exif.Exif) (*GeoFields, error) {

	F := new(GeoFields)

	LatVal, err := E.Get("GPSLatitude")
	if err != nil {
		return nil, err
	}
	F.Lat = FormatGPS(LatVal)

	LongVal, err := E.Get("GPSLongitude")
	if err != nil {
		return nil, err
	}
	F.Long = FormatGPS(LongVal)

	//Need to add more comments. Would help
	LatRefVal, err := E.Get("GPSLatitudeRef") //Lat and LatRef
	if err != nil {
		return nil, err
	}
	F.LatRef = LatRefVal.String()

	LongRefVal, err := E.Get("GPSLongitudeRef")
	if err != nil {
		return nil, err
	}

	F.LongRef = LongRefVal.String()

	// *** Longitude

	return F, nil
}
Beispiel #4
0
// Parse the date string from an exif tag and return the year, month and day
// in the format YYYYMMDD.
func (i *ImageObj) getDatePath(tags *exif.Exif) (string, error) {
	value, err := tags.Get(exif.DateTimeOriginal)
	if err != nil {
		return "", err
	}

	dateStr, err := value.StringVal()
	if err != nil {
		return "", err
	}

	dateSlice := strings.SplitN(dateStr, ":", 3)

	return path.Join(dateSlice[0], dateSlice[1]), nil
}
Beispiel #5
0
func ImageOrientation(ii *ImageInfo, x *exif.Exif) {
	orientation, err := x.Get(exif.Orientation)
	if err != nil {
		log.Println("o err: ", err)
		return
	}
	or := 0
	if orientation.Val[0] != 0 {
		or = int(orientation.Val[0])
	}
	if orientation.Val[1] != 0 {
		or = int(orientation.Val[1])
	}
	ii.Orientation = or
}
Beispiel #6
0
// This is basically a copy of the exif.Exif.DateTime() method, except:
//   * it takes a *time.Location to assume
//   * the caller already assumes there's no timezone offset or GPS time
//     in the EXIF, so any of that code can be ignored.
func exifDateTimeInLocation(x *exif.Exif, loc *time.Location) (time.Time, error) {
	tag, err := x.Get(exif.DateTimeOriginal)
	if err != nil {
		tag, err = x.Get(exif.DateTime)
		if err != nil {
			return time.Time{}, err
		}
	}
	if tag.Format() != tiff.StringVal {
		return time.Time{}, errors.New("DateTime[Original] not in string format")
	}
	const exifTimeLayout = "2006:01:02 15:04:05"
	dateStr := strings.TrimRight(string(tag.Val), "\x00")
	return time.ParseInLocation(exifTimeLayout, dateStr, loc)
}
Beispiel #7
0
func tagInt(ex *exif.Exif, ts exif.FieldName) (val int32, err error) {
	val = -1
	tg, err := ex.Get(ts)
	if err != nil {
		return
	}
	if tg.Count != 1 {
		err = errors.New(fmt.Sprintf("Tag count not 1: %s", ts))
		return
	}
	int_val, err := tg.Int(0)
	if err == nil {
		val = int32(int_val)
	}
	return
}
Beispiel #8
0
// Parse decodes all Nikon makernote data found in x and adds it to x.
func (_ *nikonV3) Parse(x *exif.Exif) error {
	m, err := x.Get(exif.MakerNote)
	if err != nil {
		return nil
	} else if bytes.Compare(m.Val[:6], []byte("Nikon\000")) != 0 {
		return nil
	}

	// Nikon v3 maker note is a self-contained IFD (offsets are relative
	// to the start of the maker note)
	mkNotes, err := tiff.Decode(bytes.NewReader(m.Val[10:]))
	if err != nil {
		return err
	}
	x.LoadTags(mkNotes.Dirs[0], makerNoteNikon3Fields, false)
	return nil
}
Beispiel #9
0
func NewGeoPhotoFromExif(exifData *exif.Exif) GeoPhoto {
	GPSLatitude, _ := exifData.Get(exif.GPSLatitude)
	GPSLatitudeRef, _ := exifData.Get(exif.GPSLatitudeRef)
	GPSLongitude, _ := exifData.Get(exif.GPSLongitude)
	GPSLongitudeRef, _ := exifData.Get(exif.GPSLongitudeRef)
	GPSTimeStamp, _ := exifData.Get(exif.GPSTimeStamp)
	GPSDateStamp, _ := exifData.Get(exif.GPSDateStamp)

	return GeoPhoto{
		GPSLatitude:     GPSLatitude,
		GPSLatitudeRef:  GPSLatitudeRef,
		GPSLongitude:    GPSLongitude,
		GPSLongitudeRef: GPSLongitudeRef,
		GPSTimeStamp:    GPSTimeStamp,
		GPSDateStamp:    GPSDateStamp,
	}
}
Beispiel #10
0
// Generate a thumbnail from the given image, save it to S3, and save the record to the DB.
func SaveThumbnail(
	proc func(image.Image, int, int, imaging.ResampleFilter) *image.NRGBA,
	photoId int32,
	photoImage image.Image,
	ex *exif.Exif,
	width, height int32) {

	thumbnail := proc(photoImage, int(width), int(height), imaging.Lanczos)

	// If the EXIF says to, rotate the thumbnail.
	var orientation int = 1
	if ex != nil {
		if orientationTag, err := ex.Get(exif.Orientation); err == nil {
			orientation = int(orientationTag.Int(0))
		}
		if rotateFunc, ok := REORIENTATION_FUNCS[orientation]; ok && rotateFunc != nil {
			thumbnail = rotateFunc(thumbnail)
		}
	}

	var thumbnailBuffer bytes.Buffer
	err := jpeg.Encode(&thumbnailBuffer, thumbnail, nil)
	if err != nil {
		rev.ERROR.Println("Failed to create thumbnail:", err)
		return
	}

	thumbnailModel := &models.Thumbnail{
		PhotoId: photoId,
		Width:   width,
		Height:  height,
	}

	err = PHOTO_BUCKET.PutReader(thumbnailModel.S3Path(),
		&thumbnailBuffer,
		int64(thumbnailBuffer.Len()),
		"image/jpeg",
		s3.PublicRead)
	if err != nil {
		rev.ERROR.Println("Failed to create thumbnail:", err)
		return
	}

	dbm.Insert(thumbnailModel)
}
Beispiel #11
0
// Parse decodes all Canon makernote data found in x and adds it to x.
func (_ *canon) Parse(x *exif.Exif) error {
	m, err := x.Get(exif.MakerNote)
	if err != nil {
		return nil
	}

	mk, err := x.Get(exif.Make)
	if err != nil {
		return nil
	}

	if val, err := mk.StringVal(); err != nil || val != "Canon" {
		return nil
	}

	// Canon notes are a single IFD directory with no header.
	// Reader offsets need to be w.r.t. the original tiff structure.
	buf := bytes.NewReader(append(make([]byte, m.ValOffset), m.Val...))
	buf.Seek(int64(m.ValOffset), 0)

	mkNotesDir, _, err := tiff.DecodeDir(buf, x.Tiff.Order)
	if err != nil {
		return err
	}
	x.LoadTags(mkNotesDir, makerNoteCanonFields, false)
	return nil
}
Beispiel #12
0
func (info *ExifInfo) Decode(x *exif.Exif) {

	// normally, don't ignore errors!
	camModel, _ := x.Get(exif.Model)
	info.CameraModel, _ = camModel.StringVal()

	// retrieve first (only) rat. value
	focal, _ := x.Get(exif.FocalLength)
	numer, _, _ := focal.Rat2(0)
	//fmt.Printf("\nFocal : %v/%v", numer, denom)
	info.Focal = numer

	// retrieve first (only) rat. value
	aperture, _ := x.Get(exif.FNumber)
	numer, _, _ = aperture.Rat2(0)
	//fmt.Printf("\nAperture : %v/%v", numer, denom)
	info.Aperture = numer

	iso, _ := x.Get(exif.ISOSpeedRatings)
	//fmt.Printf("\n%v", iso)
	info.ISO = iso.String()

}
Beispiel #13
0
func storeImage(rw http.ResponseWriter, req *http.Request) {
	// Appengine
	var c appengine.Context
	// Google Cloud Storage authentication
	var cc gcscontext.Context
	// Google Cloud Storage bucket name
	var bucketName string = ""
	// Google Cloud Storage client
	var client *storage.Client
	// Google Cloud Storage bucket
	var bucketHandle *storage.BucketHandle
	// User uploaded image file name
	var fileName string = uuid.New()
	// Transform user uploaded image to a thumbnail file name
	var fileNameThumbnail string = uuid.New()
	// User uploaded image file type
	var contentType string = ""
	// User uploaded image file raw data
	var b []byte
	// Google Cloud Storage file writer
	var wc *storage.Writer = nil
	// Error
	var err error = nil
	// Result, 0: success, 1: failed
	var r int = http.StatusCreated

	// Set response in the end
	defer func() {
		// Return status. WriteHeader() must be called before call to Write
		if r == http.StatusCreated {
			// Changing the header after a call to WriteHeader (or Write) has no effect.
			// rw.Header().Set("Location", req.URL.String()+"/"+cKey.Encode())
			rw.Header().Set("Location", "http://"+bucketName+".storage.googleapis.com/"+fileName)
			rw.Header().Set("X-Thumbnail", "http://"+bucketName+".storage.googleapis.com/"+fileNameThumbnail)
			rw.WriteHeader(r)
		} else {
			http.Error(rw, http.StatusText(r), r)
		}
	}()

	// To log information in Google APP Engine console
	c = appengine.NewContext(req)

	// Get data from body
	b, err = ioutil.ReadAll(req.Body)
	if err != nil {
		c.Errorf("%s in reading body", err)
		r = http.StatusInternalServerError
		return
	}
	c.Infof("Body length %d bytes, read %d bytes", req.ContentLength, len(b))

	// Determine filename extension from content type
	contentType = req.Header["Content-Type"][0]
	switch contentType {
	case "image/jpeg":
		fileName += ".jpg"
		fileNameThumbnail += ".jpg"
	default:
		c.Errorf("Unknown or unsupported content type '%s'. Valid: image/jpeg", contentType)
		r = http.StatusBadRequest
		return
	}
	c.Infof("Content type %s is received, %s is detected.", contentType, http.DetectContentType(b))

	// Prepare Google Cloud Storage authentication
	cc = gcsappengine.NewContext(req)
	if client, err = storage.NewClient(cc); err != nil {
		c.Errorf("%s in initializing a GCS client", err)
		r = http.StatusInternalServerError
		return
	}
	defer client.Close()

	// Get default bucket
	if bucketName, err = gcsfile.DefaultBucketName(cc); err != nil {
		c.Errorf("%s in getting default GCS bucket name", err)
		r = http.StatusInternalServerError
		return
	}
	bucketHandle = client.Bucket(bucketName)
	c.Infof("APP Engine Version: %s", gcsappengine.VersionID(cc))
	c.Infof("Using bucket name: %s", bucketName)

	// Change default object ACLs
	if err = bucketHandle.DefaultObjectACL().Set(cc, storage.AllUsers, storage.RoleReader); err != nil {
		c.Errorf("%v in saving default object ACL rule for bucket %q", err, bucketName)
		r = http.StatusInternalServerError
		return
	}

	// Store rotated image in Google Cloud Storage
	var in *bytes.Reader = bytes.NewReader(b)
	var x *exif.Exif = nil
	var orientation *tiff.Tag = nil
	var beforeImage image.Image
	var afterImage *image.NRGBA = nil

	// Read EXIF
	if _, err = in.Seek(0, 0); err != nil {
		c.Errorf("%s in moving the reader offset to the beginning in order to read EXIF", err)
		return
	}
	if x, err = exif.Decode(in); err != nil {
		c.Errorf("%s in decoding JPEG image", err)
		return
	}

	// Get Orientation
	if orientation, err = x.Get(exif.Orientation); err != nil {
		c.Warningf("%s in getting orientation from EXIF", err)
		return
	}
	c.Debugf("Orientation %s", orientation.String())

	// Open image
	if _, err = in.Seek(0, 0); err != nil {
		c.Errorf("%s in moving the reader offset to the beginning in order to read EXIF", err)
		return
	}
	if beforeImage, err = imaging.Decode(in); err != nil {
		c.Errorf("%s in opening image %s", err)
		return
	}

	switch orientation.String() {
	case "1":
		afterImage = beforeImage.(*image.NRGBA)
	case "2":
		afterImage = imaging.FlipH(beforeImage)
	case "3":
		afterImage = imaging.Rotate180(beforeImage)
	case "4":
		afterImage = imaging.FlipV(beforeImage)
	case "5":
		afterImage = imaging.Transverse(beforeImage)
	case "6":
		afterImage = imaging.Rotate270(beforeImage)
	case "7":
		afterImage = imaging.Transpose(beforeImage)
	case "8":
		afterImage = imaging.Rotate90(beforeImage)
	}

	// Save rotated image
	wc = bucketHandle.Object(fileName).NewWriter(cc)
	wc.ContentType = contentType
	if err = imaging.Encode(wc, afterImage, imaging.JPEG); err != nil {
		c.Errorf("%s in saving rotated image", err)
		return
	}
	if err = wc.Close(); err != nil {
		c.Errorf("CreateFile: unable to close bucket %q, file %q: %v", bucketName, fileName, err)
		r = 1
		return
	}
	wc = nil

	// Make thumbnail
	if afterImage.Rect.Dx() > afterImage.Rect.Dy() {
		afterImage = imaging.Resize(afterImage, 1920, 0, imaging.Lanczos)
	} else {
		afterImage = imaging.Resize(afterImage, 0, 1920, imaging.Lanczos)
	}

	// Save thumbnail
	wc = bucketHandle.Object(fileNameThumbnail).NewWriter(cc)
	wc.ContentType = contentType
	if imaging.Encode(wc, afterImage, imaging.JPEG); err != nil {
		c.Errorf("%s in saving image thumbnail", err)
		return
	}
	if err = wc.Close(); err != nil {
		c.Errorf("CreateFileThumbnail: unable to close bucket %q, file %q: %v", bucketName, fileNameThumbnail, err)
		r = 1
		return
	}

	c.Infof("/%v/%v, /%v/%v created", bucketName, fileName, bucketName, fileNameThumbnail)
}