3
votes

Language: Swift 3

What I am using: Hardware: An IPhone 6 and FLIR One thermal camera Software: Xcode 8.1, Locked down FLIROne SDK (written and documented for obj-c and has little documentation at all)

What I am trying to do: Get the temperature of the center pixel from the returned camera stream

The problem: I have everything in the app I am building setup and working as expected not including getting this thermal data. I have been able to get temperature from the returned stream from the camera, however I can only get it for the 0,0 (top left) pixel. I have crosshairs in the center of the image view that the stream feeds into and I would like to get the pixel at this center point (the exact center of the image). I have been working on this for two days and I can't seem to figure out how to do this. The SDK does not allow you to specify which pixel to read from and from what I have read on online, you have to loop through each pixel and stop at the center one.

The below code will get the temperature from pixel 0,0 and display it correctly. I want to get temperature to read center pixel instead. temperature MUST be UInt16 to provide correct Kelvin readout (from what I understand at least), but I receive the radiometric data from Data! as UInt8. NOTE: NSData! does not work with these delegates. Attempting to use it (even in swift 2.3) causes the delegate to never fire. I have to use Data! for it to even run the delegate. I found it very odd that I couldn't use swift 2.3 because it only has NSData. If this is because I screwed something up please let me know.

func flirOneSDKDelegateManager(_ delegateManager: FLIROneSDKDelegateManager!, didReceiveRadiometricData radiometricData: Data!, imageSize size: CGSize) {

    let byteArray = radiometricData?.withUnsafeBytes{
        [UInt8](UnsafeBufferPointer(start: $0, count: (radiometricData?.count)!))
    }

    let temperature = UnsafePointer(byteArray!).withMemoryRebound(to: UInt16.self, capacity: 1){
        $0.pointee
    }

    debugPrint(temperature)

    DispatchQueue.main.async{
        self.tempLabel.text = NSString(format: "%.1f",self.convertToFarenheit(kelvin: temperature)) as String
    }
}

I am new to Swift so I am not sure if this is the best way to do this so if you have a better way please advise me on it.

Current solution: Gets the center pixel, but not dynamically (which is what I want)

    let byteArray = radiometricData?.withUnsafeBytes{
        [UInt8](UnsafeBufferPointer(start: $0, count: (radiometricData?.count)!))
    }

    let bytes:[UInt8] = [(byteArray?[74170])!,(byteArray?[74171])!]

    let temperature = UnsafePointer(bytes).withMemoryRebound(to: UInt16.self, capacity: 1){
        $0.pointee
    }
1

1 Answers

1
votes

assuming you try to read some UInt16 values from swift's Data

import Foundation

var data = Data([1,0,2,0,255,255,1]) // 7 bytes
var arr: [UInt16] = []

// in my data only 6 bytes could be represented as Int16 values, so i have to ignore the last one ...
for i in stride(from: 0, to: 2 * (data.count / 2) , by: MemoryLayout<UInt16>.stride) {
    arr.append(data.subdata(in: i..<(i+MemoryLayout<UInt16>.stride)).withUnsafeBytes { $0.pointee })
}

print(arr) // [1, 2, 65535]

or you could use something like

let arr0: [UInt16] = data.withUnsafeBytes { (p: UnsafePointer<UInt8>)->[UInt16] in
    let capacity = data.count / MemoryLayout<UInt16>.stride
    return p.withMemoryRebound(to: UInt16.self, capacity: capacity) {
        var arr = [UInt16]()
        for i in 0..<capacity {
            arr.append(($0 + i).pointee)
        }
        return arr
    }
}
print(arr0) // [1, 2, 65535]

or

extension Data {
    func scan<T>(offset: Int, bytes: Int) -> T {
        return self.subdata(in: offset..<(offset+bytes)).withUnsafeBytes { $0.pointee }
    }
}

let k: UInt16 = data.scan(offset: 2, bytes: MemoryLayout<UInt16>.stride)
print(k) // 2

or even better

extension Data {
    func scan<T>(from: Int)->T {
        return self.withUnsafeBytes { (p: UnsafePointer<UInt8>)->T in
            p.advanced(by: from).withMemoryRebound(to: T.self, capacity: 1) {
                $0.pointee
            }
        }
    }
}
let u0: UInt16 = data.scan(from: 2)
print(u0) // 2

or

    extension Data {
        func scan2<T>(from: Int)->T {
            return self.withUnsafeBytes { 
// Precondition: The underlying pointer plus offset is properly aligned for accessing T.
// Precondition: The memory is initialized to a value of some type, U, such that T is layout compatible with U.
                 UnsafeRawPointer($0).load(fromByteOffset: from, as: T.self)
            }
        }
    }

    let u2: UInt16 = data.scan2(from: 2)
    print(u2) // 2

what is the right offset in your Data, to read the value? it is hard to say from the information you provided in your question.