added other image metadata
This commit is contained in:
parent
a51aa1ef97
commit
25cd72c5f4
|
@ -59,15 +59,8 @@ public class FileHandling {
|
|||
}
|
||||
|
||||
/// Unpack image metadata after a file was opened
|
||||
public static func metaData(rawdata: UnsafeMutablePointer<libraw_data_t>) {
|
||||
|
||||
// To be done
|
||||
|
||||
}
|
||||
|
||||
/// Returns image paraneters from opened file
|
||||
public static func imageParameters(rawdata : UnsafeMutablePointer<libraw_data_t> ) -> ImageParameters {
|
||||
return ImageParameters(iparams: rawdata.pointee.idata)
|
||||
public static func metaData(rawdata: UnsafeMutablePointer<libraw_data_t>) -> MetaDataInformation {
|
||||
return MetaDataInformation(rawdata)
|
||||
}
|
||||
|
||||
/// Unpacked Images must be released at end of processing
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// ImageOtherInformation.swift
|
||||
// SwiftLibRaw
|
||||
//
|
||||
// Created by Thorsten Claus on 08.01.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libraw
|
||||
|
||||
/// Other image information like ISO, Aperture, Shutter Time
|
||||
public struct ImageOtherInformation {
|
||||
|
||||
public let analogBalance : [Float]
|
||||
|
||||
/// ISO sensitivity.
|
||||
public let iso_speed : Float
|
||||
|
||||
/// Shutter speed in seconds
|
||||
public let shutter : Float
|
||||
|
||||
/// Aperture
|
||||
public let aperture: Float
|
||||
|
||||
public let description: String
|
||||
///Focal length
|
||||
public let focal_length: Float
|
||||
|
||||
/// Date of shooting.
|
||||
public let shooted_at: Date
|
||||
|
||||
/// Serial number of image.
|
||||
public let shot_order : Int
|
||||
|
||||
/// GPS data (unparsed block, to write to output as is).
|
||||
// public let unsigned gpsdata[32]
|
||||
|
||||
/// Parsed GPS-data: longitude/latitude/altitude and time stamp.
|
||||
// libraw_gps_info_t parsed_gps;
|
||||
// char desc[512];
|
||||
|
||||
/// Author of image.
|
||||
public let artist : String
|
||||
|
||||
|
||||
init(otherInformation: libraw_imgother_t) {
|
||||
|
||||
analogBalance = withUnsafePointer(to: otherInformation.analogbalance){
|
||||
$0.withMemoryRebound(to: Float.self, capacity: 36) {
|
||||
return [Float](UnsafeBufferPointer(start: $0, count: MemoryLayout.size(ofValue: otherInformation.analogbalance)))
|
||||
}
|
||||
}
|
||||
|
||||
aperture = otherInformation.aperture
|
||||
artist = withUnsafePointer(to: otherInformation.artist) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
description = withUnsafePointer(to: otherInformation.desc) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
focal_length = otherInformation.focal_len
|
||||
|
||||
// TODO: Implement detailed GPS Data
|
||||
// otherInformation.parsed_gps
|
||||
// otherInformation.gpsdata
|
||||
|
||||
iso_speed = Float(otherInformation.iso_speed)
|
||||
shot_order = Int(otherInformation.shot_order)
|
||||
shutter = otherInformation.shutter
|
||||
shooted_at = Date(timeIntervalSince1970: Double(otherInformation.timestamp))
|
||||
}
|
||||
}
|
|
@ -5,33 +5,34 @@
|
|||
// Created by Thorsten Claus on 07.01.21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
import libraw
|
||||
|
||||
/// Meta Data about Camera and Image
|
||||
public struct ImageParameters {
|
||||
|
||||
public var camera_manufacturer : String?
|
||||
/// There is a huge number of identical cameras sold under different names, depending on the market (e.g. multiple Panasonic or Canon models) and even some identical cameras sold under different brands (Panasonic -> Leica, Sony -> Hasselblad). normalized_make contains primary vendor name (e.g. Panasonic for Leica re-branded cameras).
|
||||
public var camera_normalized_manufacturer : String?
|
||||
|
||||
public var camera_model : String?
|
||||
public let camera_manufacturer : String
|
||||
/// There is a huge number of identical cameras sold under different names, depending on the market (e.g. multiple Panasonic or Canon models) and even some identical cameras sold under different brands (Panasonic -> Leica, Sony -> Hasselblad). normalized_make contains primary vendor name (e.g. Panasonic for Leica re-branded cameras).
|
||||
public let camera_normalized_manufacturer : String
|
||||
|
||||
public let camera_model : String
|
||||
/// Primary camera model name.
|
||||
public var camera_normalized_model : String?
|
||||
public let camera_normalized_model : String
|
||||
|
||||
/// Softwary name/version (mostly for DNG files, to distinguish in-camera DNGs from Adobe DNG Converter produced ones).
|
||||
public var software : String?
|
||||
public let software : String
|
||||
|
||||
/// Number of RAW images in file (0 means that the file has not been recognized).
|
||||
public var raw_count : Int = 0
|
||||
public let raw_count : Int
|
||||
|
||||
/// Number of colors in the file
|
||||
public var number_of_colors : Int
|
||||
public let number_of_colors : Int
|
||||
|
||||
/// Description of colors numbered from 0 to 3 (RGBG,RGBE,GMCY, or GBTG)
|
||||
public var color_description : String
|
||||
public let color_description : String
|
||||
|
||||
/// Nonzero for Sigma Foveon images
|
||||
public var isFOV_on : Int
|
||||
public let isFOV_on : Int
|
||||
|
||||
/*
|
||||
Bit mask describing the order of color pixels in the matrix (0 for full-color images). 32 bits of this field describe 16 pixels (8 rows with two pixels in each, from left to right and from top to bottom). Each two bits have values 0 to 3, which correspond to four possible colors. Convenient work with this field is ensured by the COLOR(row,column) function, which returns the number of the active color for a given pixel.
|
||||
|
@ -49,61 +50,61 @@ public struct ImageParameters {
|
|||
public var xtrans_abs_6x6 : [UInt8] = Array(repeating: 0, count: 6 * 6)
|
||||
|
||||
|
||||
init(iparams : libraw_iparams_t) {
|
||||
init(parameters: libraw_iparams_t) {
|
||||
|
||||
camera_manufacturer = withUnsafePointer(to: iparams.make) {
|
||||
camera_manufacturer = withUnsafePointer(to: parameters.make) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
camera_normalized_manufacturer = withUnsafePointer(to: iparams.normalized_make) {
|
||||
camera_normalized_manufacturer = withUnsafePointer(to: parameters.normalized_make) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
camera_model = withUnsafePointer(to: iparams.model) {
|
||||
camera_model = withUnsafePointer(to: parameters.model) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
camera_normalized_model = withUnsafePointer(to: iparams.normalized_model) {
|
||||
camera_normalized_model = withUnsafePointer(to: parameters.normalized_model) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
software = withUnsafePointer(to: iparams.software) {
|
||||
software = withUnsafePointer(to: parameters.software) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
raw_count = Int(iparams.raw_count)
|
||||
raw_count = Int(parameters.raw_count)
|
||||
|
||||
number_of_colors = Int(iparams.colors)
|
||||
filters = Int(iparams.filters)
|
||||
number_of_colors = Int(parameters.colors)
|
||||
filters = Int(parameters.filters)
|
||||
|
||||
color_description = withUnsafePointer(to: iparams.cdesc) {
|
||||
color_description = withUnsafePointer(to: parameters.cdesc) {
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
|
||||
String(cString: $0)
|
||||
}
|
||||
}
|
||||
|
||||
isFOV_on = Int(iparams.is_foveon)
|
||||
isFOV_on = Int(parameters.is_foveon)
|
||||
|
||||
// 6x6 matrix
|
||||
xtrans_6x6 = withUnsafePointer(to: iparams.xtrans){
|
||||
xtrans_6x6 = withUnsafePointer(to: parameters.xtrans){
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: 36) {
|
||||
return [UInt8](UnsafeBufferPointer(start: $0, count: MemoryLayout.size(ofValue: iparams.xtrans)))
|
||||
return [UInt8](UnsafeBufferPointer(start: $0, count: MemoryLayout.size(ofValue: parameters.xtrans)))
|
||||
}
|
||||
}
|
||||
// 6x6 Matrix
|
||||
xtrans_abs_6x6 = withUnsafePointer(to: iparams.xtrans_abs){
|
||||
xtrans_abs_6x6 = withUnsafePointer(to: parameters.xtrans_abs){
|
||||
$0.withMemoryRebound(to: UInt8.self, capacity: 36) {
|
||||
return [UInt8](UnsafeBufferPointer(start: $0, count: MemoryLayout.size(ofValue: iparams.xtrans_abs)))
|
||||
return [UInt8](UnsafeBufferPointer(start: $0, count: MemoryLayout.size(ofValue: parameters.xtrans_abs)))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// ImageSizes.swift
|
||||
// SwiftLibRaw
|
||||
//
|
||||
// Created by Thorsten Claus on 07.01.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libraw
|
||||
|
||||
/// The structure describes the geometrical parameters of the image.
|
||||
public struct ImageSizes {
|
||||
|
||||
/// Full size of RAW image (including the frame) in pixels.
|
||||
public let heigth, width : UInt16
|
||||
|
||||
/// Size of visible ("meaningful") part of the image (without the frame).
|
||||
public let raw_heigth, raw_width : UInt16
|
||||
|
||||
/// Coordinates of the top left corner of the frame (the second corner is calculated from the full size of the image and size of its visible part)
|
||||
public let top_margin, left_margin : UInt16
|
||||
|
||||
/// Size of the output image (may differ from height/width for cameras that require image rotation or have non-square pixels).
|
||||
public let iheight, iwidth : UInt16
|
||||
|
||||
/// Full size of raw data row in bytes
|
||||
public let raw_pitch : UInt32
|
||||
|
||||
/// Pixel width/height ratio. If it is not unity, scaling of the image along one of the axes is required during output.
|
||||
public let pixel_aspect : Double
|
||||
|
||||
/// Image orientation (0 if does not require rotation; 3 if requires 180-deg rotation; 5 if 90 deg counterclockwise, 6 if 90 deg clockwise).
|
||||
public let flip: Int
|
||||
|
||||
public init(sizes: libraw_image_sizes_t) {
|
||||
heigth = sizes.height
|
||||
width = sizes.width
|
||||
|
||||
raw_heigth = sizes.raw_width
|
||||
raw_width = sizes.raw_height
|
||||
|
||||
top_margin = sizes.top_margin
|
||||
left_margin = sizes.left_margin
|
||||
|
||||
iheight = sizes.iheight
|
||||
iwidth = sizes.iwidth
|
||||
|
||||
raw_pitch = sizes.raw_pitch
|
||||
pixel_aspect = sizes.pixel_aspect
|
||||
|
||||
flip = Int(sizes.flip) // TODO: Build as enum
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// Lenses.swift
|
||||
// SwiftLibRaw
|
||||
//
|
||||
// Created by Thorsten Claus on 07.01.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libraw
|
||||
|
||||
/// Lensinfo
|
||||
public struct Lense {
|
||||
|
||||
init(lensInfo: libraw_lensinfo_t) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// MetaDataInformation.swift
|
||||
// SwiftLibRaw
|
||||
//
|
||||
// Created by Thorsten Claus on 07.01.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libraw
|
||||
|
||||
public struct MetaDataInformation {
|
||||
public let lensinfo : Lense
|
||||
public let sizes : ImageSizes
|
||||
public let parameters : ImageParameters
|
||||
public let otherInformation : ImageOtherInformation
|
||||
|
||||
init(_ rawdata: UnsafeMutablePointer<libraw_data_t>) {
|
||||
|
||||
let data = rawdata.pointee
|
||||
|
||||
lensinfo = Lense(lensInfo: data.lens)
|
||||
sizes = ImageSizes(sizes: data.sizes)
|
||||
parameters = ImageParameters(parameters: data.idata)
|
||||
otherInformation = ImageOtherInformation(otherInformation: data.other)
|
||||
}
|
||||
}
|
|
@ -13,14 +13,7 @@ import libraw
|
|||
|
||||
class TestFileHandling : XCTestCase {
|
||||
|
||||
// During Test its not the main bundle
|
||||
var testfilePath : URL! {
|
||||
get {
|
||||
let bundle = Bundle.init(for: TestFileHandling.self)
|
||||
return bundle.url(forResource: "Moon", withExtension: "RAF")!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func testOpenFile_notFound() {
|
||||
let rawdata = libraw_init(0)!;
|
||||
let fileOpenresult = FileHandling.openFile(fileUrl: URL(fileURLWithPath: ""),rawdata: rawdata)
|
||||
|
@ -45,31 +38,5 @@ class TestFileHandling : XCTestCase {
|
|||
let unpackResult = FileHandling.unpackFile(rawdata: rawdata)
|
||||
XCTAssertEqual(unpackResult, LIBRAW_SUCCESS)
|
||||
}
|
||||
|
||||
func testgetImageParameters() {
|
||||
let rawdata = libraw_init(0)!;
|
||||
let fileOpenresult = FileHandling.openFile(fileUrl: testfilePath,rawdata: rawdata)
|
||||
XCTAssertEqual(fileOpenresult, LIBRAW_SUCCESS)
|
||||
|
||||
let imageData : ImageParameters = FileHandling.imageParameters(rawdata: rawdata)
|
||||
|
||||
XCTAssertNotNil(imageData)
|
||||
XCTAssertEqual(imageData.camera_manufacturer, "Fujifilm")
|
||||
XCTAssertEqual(imageData.camera_model, "X-E2")
|
||||
|
||||
XCTAssertNotNil(imageData.camera_normalized_model)
|
||||
XCTAssertNotNil(imageData.camera_normalized_manufacturer)
|
||||
XCTAssertTrue(imageData.number_of_colors > 0)
|
||||
XCTAssertTrue(imageData.raw_count > 0) // 0 means file could not be read
|
||||
XCTAssertNotNil(imageData.color_description)
|
||||
|
||||
print("Camera: \(imageData.camera_manufacturer!) \(imageData.camera_model!)")
|
||||
print("Software: \(imageData.software!)")
|
||||
print("Filter: \(imageData.filters)")
|
||||
print("Number of colors: \(imageData.number_of_colors)")
|
||||
print("Color description: \(imageData.color_description)")
|
||||
print("Fuji Xtrans: \(imageData.xtrans_6x6)")
|
||||
print("Fuji Xtrans (Sensor-Edge relative): \(imageData.xtrans_abs_6x6)")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// TestMetadata.swift
|
||||
// SwiftLibRawTests
|
||||
//
|
||||
// Created by Thorsten Claus on 08.01.21.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Foundation
|
||||
import libraw
|
||||
|
||||
class TestMetadata: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testgetImageParameters() {
|
||||
let rawdata = libraw_init(0)!;
|
||||
let fileOpenresult = FileHandling.openFile(fileUrl: testfilePath,rawdata: rawdata)
|
||||
XCTAssertEqual(fileOpenresult, LIBRAW_SUCCESS)
|
||||
|
||||
let imageData : ImageParameters = ImageParameters(parameters: rawdata.pointee.idata)
|
||||
|
||||
XCTAssertNotNil(imageData)
|
||||
XCTAssertEqual(imageData.camera_manufacturer, "Fujifilm")
|
||||
XCTAssertEqual(imageData.camera_model, "X-E2")
|
||||
|
||||
XCTAssertNotNil(imageData.camera_normalized_model)
|
||||
XCTAssertNotNil(imageData.camera_normalized_manufacturer)
|
||||
XCTAssertTrue(imageData.number_of_colors > 0)
|
||||
XCTAssertTrue(imageData.raw_count > 0) // 0 means file could not be read
|
||||
XCTAssertEqual(imageData.color_description, "RGBG")
|
||||
|
||||
print("Camera: \(imageData.camera_manufacturer) \(imageData.camera_model)")
|
||||
print("Software: \(imageData.software)")
|
||||
print("Filter: \(imageData.filters)")
|
||||
print("Number of colors: \(imageData.number_of_colors)")
|
||||
print("Color description: \(imageData.color_description)")
|
||||
print("Fuji Xtrans: \(imageData.xtrans_6x6)")
|
||||
print("Fuji Xtrans (Sensor-Edge relative): \(imageData.xtrans_abs_6x6)")
|
||||
}
|
||||
|
||||
func testImageSizes() {
|
||||
let rawdata = libraw_init(0)!;
|
||||
let fileOpenresult = FileHandling.openFile(fileUrl: testfilePath,rawdata: rawdata)
|
||||
XCTAssertEqual(fileOpenresult, LIBRAW_SUCCESS)
|
||||
|
||||
let imageSizes : ImageSizes = ImageSizes(sizes: rawdata.pointee.sizes)
|
||||
XCTAssertNotNil(imageSizes)
|
||||
|
||||
XCTAssertEqual(imageSizes.heigth, 3296)
|
||||
XCTAssertEqual(imageSizes.width, 4934)
|
||||
XCTAssertEqual(imageSizes.raw_heigth, 4992)
|
||||
XCTAssertEqual(imageSizes.raw_width, 3296)
|
||||
XCTAssertEqual(imageSizes.top_margin, 0)
|
||||
XCTAssertEqual(imageSizes.left_margin, 6)
|
||||
XCTAssertEqual(imageSizes.iheight, 3296)
|
||||
XCTAssertEqual(imageSizes.iwidth, 4934)
|
||||
|
||||
print("Image size: w/h \(imageSizes.width)x\(imageSizes.heigth)")
|
||||
}
|
||||
|
||||
func testMetaInformation() {
|
||||
let rawdata = libraw_init(0)!;
|
||||
let fileOpenresult = FileHandling.openFile(fileUrl: testfilePath,rawdata: rawdata)
|
||||
XCTAssertEqual(fileOpenresult, LIBRAW_SUCCESS)
|
||||
|
||||
let metaInformation = MetaDataInformation(rawdata)
|
||||
XCTAssertNotNil(metaInformation)
|
||||
XCTAssertNotNil(metaInformation.parameters)
|
||||
XCTAssertNotNil(metaInformation.sizes)
|
||||
XCTAssertNotNil(metaInformation.lensinfo)
|
||||
XCTAssertNotNil(metaInformation.otherInformation)
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// TestSetup.swift
|
||||
// SwiftLibRawTests
|
||||
//
|
||||
// Created by Thorsten Claus on 08.01.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// During Test its not the main bundle
|
||||
var testfilePath : URL! {
|
||||
get {
|
||||
let bundle = Bundle.init(for: TestFileHandling.self)
|
||||
return bundle.url(forResource: "Moon", withExtension: "RAF")!
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue