Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Nikon maker note parsing. #18

Open
wants to merge 1 commit into
base: go1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 47 additions & 26 deletions mknote/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const (
ImageAuthentication = "ImageAuthentication"
ActiveDLighting = "ActiveDLighting"
VignetteControl = "VignetteControl"
ImageAdjustment = "ImageAdjustment"
ToneComp = "ToneComp"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you've brought attention to this, it looks like the concept of "common" makernote fields don't really exist - only maker-specific fields. So perhaps we should scrap them all in favor of maker-prefixed constants. e.g. Nikon_VignetteControl.

AuxiliaryLens = "AuxiliaryLens"
LensType = "LensType"
Expand Down Expand Up @@ -101,31 +100,37 @@ const (
ColorData = "ColorData"

// Nikon-specific fields
Nikon_Version = "Nikon.Version"
Nikon_WhiteBalance = "Nikon.WhiteBalance"
Nikon_ColorSpace = "Nikon.ColorSpace"
Nikon_LightSource = "Nikon.LightSource"
Nikon_Saturation = "Nikon_Saturation"
Nikon_ShotInfo = "Nikon.ShotInfo" // A sub-IFD
Nikon_VRInfo = "Nikon.VRInfo" // A sub-IFD
Nikon_PictureControl = "Nikon.PictureControl" // A sub-IFD
Nikon_WorldTime = "Nikon.WorldTime" // A sub-IFD
Nikon_ISOInfo = "Nikon.ISOInfo" // A sub-IFD
Nikon_AFInfo = "Nikon.AFInfo" // A sub-IFD
Nikon_ColorBalance = "Nikon.ColorBalance" // A sub-IFD
Nikon_LensData = "Nikon.LensData" // A sub-IFD
Nikon_SerialNO = "Nikon.SerialNO" // usually starts with "NO="
Nikon_FlashInfo = "Nikon.FlashInfo" // A sub-IFD
Nikon_MultiExposure = "Nikon.MultiExposure" // A sub-IFD
Nikon_AFInfo2 = "Nikon.AFInfo2" // A sub-IFD
Nikon_FileInfo = "Nikon.FileInfo" // A sub-IFD
Nikon_AFTune = "Nikon.AFTune" // A sub-IFD
Nikon3_0x000a = "Nikon3.0x000a"
Nikon3_0x009b = "Nikon3.0x009b"
Nikon3_0x009f = "Nikon3.0x009f"
Nikon3_0x00a3 = "Nikon3.0x00a3"
Nikon_FamilyId = "Nikon.FamilyId"
Nikon_ImageAdjustment = "Nikon.ImageAdjustment" // Int or String
Nikon_WhiteBalance = "Nikon.WhiteBalance" // Int or String
Nikon_Focus = "Nikon.Focus" // Rational
Nikon_AuxiliaryLens = "Nikon.AuxiliaryLens" // Int or String
Nikon_Version = "Nikon.Version"
Nikon_ColorSpace = "Nikon.ColorSpace"
Nikon_LightSource = "Nikon.LightSource"
Nikon_Saturation = "Nikon_Saturation"
Nikon_ShotInfo = "Nikon.ShotInfo" // A sub-IFD
Nikon_VRInfo = "Nikon.VRInfo" // A sub-IFD
Nikon_PictureControl = "Nikon.PictureControl" // A sub-IFD
Nikon_WorldTime = "Nikon.WorldTime" // A sub-IFD
Nikon_ISOInfo = "Nikon.ISOInfo" // A sub-IFD
Nikon_AFInfo = "Nikon.AFInfo" // A sub-IFD
Nikon_ColorBalance = "Nikon.ColorBalance" // A sub-IFD
Nikon_LensData = "Nikon.LensData" // A sub-IFD
Nikon_SerialNO = "Nikon.SerialNO" // usually starts "NO="
Nikon_FlashInfo = "Nikon.FlashInfo" // A sub-IFD
Nikon_MultiExposure = "Nikon.MultiExposure" // A sub-IFD
Nikon_AFInfo2 = "Nikon.AFInfo2" // A sub-IFD
Nikon_FileInfo = "Nikon.FileInfo" // A sub-IFD
Nikon_AFTune = "Nikon.AFTune" // A sub-IFD
Nikon1_0x0009 = "Nikon1.0x0009"
Nikon1_0x0f00 = "Nikon1.0x0f00"
Nikon3_0x000a = "Nikon3.0x000a"
Nikon3_0x009b = "Nikon3.0x009b"
Nikon3_0x009f = "Nikon3.0x009f"
Nikon3_0x00a3 = "Nikon3.0x00a3"

// Canon-specific fiends
// Canon-specific fields
Canon_CameraSettings = "Canon.CameraSettings" // A sub-IFD
Canon_ShotInfo = "Canon.ShotInfo" // A sub-IFD
Canon_AFInfo = "Canon.AFInfo"
Expand Down Expand Up @@ -173,6 +178,22 @@ var makerNoteCanonFields = map[uint16]exif.FieldName{
0x4001: ColorData,
}

// Nikon version 1 Maker Notes fields (used by E700, E800, E900, E900S, E910,
// and E950)
var makerNoteNikon1Fields = map[uint16]exif.FieldName{
0x0002: Nikon_FamilyId,
0x0003: Quality,
0x0004: ColorMode,
0x0005: Nikon_ImageAdjustment,
0x0006: ISOSpeed,
0x0007: Nikon_WhiteBalance,
0x0008: Nikon_Focus,
0x0009: Nikon1_0x0009,
0x000a: DigitalZoom,
0x000b: Nikon_AuxiliaryLens,
0x0f00: Nikon1_0x0f00,
}

// Nikon version 3 Maker Notes fields (used by E5400, SQ, D2H, D70, and newer)
var makerNoteNikon3Fields = map[uint16]exif.FieldName{
0x0001: Nikon_Version,
Expand Down Expand Up @@ -210,7 +231,7 @@ var makerNoteNikon3Fields = map[uint16]exif.FieldName{
0x0024: Nikon_WorldTime,
0x0025: Nikon_ISOInfo,
0x002a: VignetteControl,
0x0080: ImageAdjustment,
0x0080: Nikon_ImageAdjustment,
0x0081: ToneComp,
0x0082: AuxiliaryLens,
0x0083: LensType,
Expand Down
35 changes: 27 additions & 8 deletions mknote/mknote.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,35 @@ 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
}
if bytes.Compare(m.Val[:8], []byte("Nikon\000\001\000")) == 0 {
// Nikon maker note type 1 is an IFD.
// 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+8), 0)

// 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
mkNotesDir, _, err := tiff.DecodeDir(buf, x.Tiff.Order)
if err != nil {
return err
}
x.LoadTags(mkNotesDir, makerNoteNikon1Fields, false)
} else if bytes.Compare(m.Val[:7], []byte("Nikon\000\002")) == 0 {
// Nikon maker note type 3 has a full TIFF header. Type 2 does not.
if bytes.Compare(m.Val[10:14], []byte("MM\000*")) == 0 ||
bytes.Compare(m.Val[10:14], []byte("II*\000")) == 0 {
// Nikon v3 maker note is a complete TIFF container (offsets are
// relative to the start of the maker note's TIFF container).
mkNotes, err := tiff.Decode(bytes.NewReader(m.Val[10:]))
if err != nil {
return err
}
x.LoadTags(mkNotes.Dirs[0], makerNoteNikon3Fields, false)
} else {
// Nikon v2 maker note is reported to start with "Nikon\000\002",
// but have no TIFF header. Others report that it lacks even the
// "Nikon..." header. Currently unimplemented until an example is
// available.
}
}
x.LoadTags(mkNotes.Dirs[0], makerNoteNikon3Fields, false)
return nil
}