So I have some code for doing depth detection on 2 images. The depth detection is happening in native C code. In order to make the call to the C library I have another function to build arrays of Int16 that I pass in as the parameters.
When I make the function calls directly in the parameters of the C call however, it overwrites the first function call and passes in the same parameter twice.
Here is what I mean, this is my call to the C library:
let result = ImageProcessing.depthDetection(leftPhoto.cgImage!,
right: rightPhoto.cgImage!,
leftFace: getFaceDetails(image: leftPhoto, face: leftFace!).mutablePointer,
rightFace: getFaceDetails(image: rightPhoto, face: rightFace!).mutablePointer))
The get face details call looks like this:
private func getFaceDetails(image: UIImage, face: VisionFace) -> [Int16] {
var details = [Int16](repeating: 0, count: 10)
// get image size
details[0] = Int16(image.size.width)
details[1] = Int16(image.size.height)
// get face bounds
details[2] = Int16(face.frame.origin.x)
details[3] = Int16(face.frame.origin.y)
details[4] = Int16(face.frame.origin.x + face.frame.width)
details[5] = Int16(face.frame.origin.y + face.frame.height)
// get eye locations
details[6] = Int16(truncating: face.landmark(ofType: .leftEye)?.position.x ?? 0)
details[7] = Int16(truncating: face.landmark(ofType: .leftEye)?.position.y ?? 0)
details[8] = Int16(truncating: face.landmark(ofType: .rightEye)?.position.x ?? 0)
details[9] = Int16(truncating: face.landmark(ofType: .rightEye)?.position.y ?? 0)
print("Details: \(details)")
return details
}
and I'm getting the mutable pointer with this extension:
extension Array where Element == Int16 {
var mutablePointer: UnsafeMutablePointer<Int16> {
get {
return UnsafeMutablePointer<Int16>(mutating: self)
}
}
}
So when I run the above ImageProcessing.depthDetection
call, I can see printed out that my leftFace
and rightFace
arrays are indeed different and look like this:
Details: [3088, 2320, 1119, 431, 2230, 1542, 1493, 888, 1892, 882]
Details: [3088, 2320, 864, 446, 1975, 1556, 1207, 900, 1626, 890]
but when i print them out in C, they are both the same the rightFace
array and look like this (I'm formatting my C logs differently but you can tell that both left and right have the same data):
0: Left (3088, 2320), Right (3088, 2320)
1: Left (864, 446), Right (864, 446)
2: Left (1975, 1556), Right (1975, 1556)
3: Left (1207, 900), Right (1207, 900)
4: Left (1626, 890), Right (1626, 890)
So how come the first getFaceDetails
output is being overridden by the second one?
The strangest part of it all is that if i assign the result of getFaceDetails
to a variable and then pass in that variable as the parameter like I'm doing here:
let lf = getFaceDetails(image: leftPhoto, face: leftFace!)
let rf = getFaceDetails(image: rightPhoto, face: rightFace!)
let result = ImageProcessing.depthDetection(leftPhoto.cgImage!,
right: rightPhoto.cgImage!,
leftFace: lf.mutablePointer,
rightFace: rf.mutablePointer)
Then all of a sudden it works! My print statements in C show different data for left and right face. It's only when I pass the function calls in the parameters directly that it has the wrong data.
So whats going on here? Why doesn't the first way im doing it yield the same results as the latter?
As several of the comment have pointed out, this is undefined behavior:
extension Array where Element == Int16 {
var mutablePointer: UnsafeMutablePointer<Int16> {
get {
return UnsafeMutablePointer<Int16>(mutating: self)
}
}
}
As soon as UnsafeMutablePointer.init(mutating:)
returns, the pointer it provides is meaningless. What you mean is something along these lines:
let result = lf.withUnsafeMutableBytes { lfBytes in
rf.withUnsafeMutableBytes { rfBytes in
return ImageProcessing.depthDetection(leftPhoto.cgImage!,
right: rightPhoto.cgImage!,
leftFace: lfBytes,
rightFace: rhBytes)
}
}
Alternately, you might be able to do something along these lines:
let result = ImageProcessing.depthDetection(leftPhoto.cgImage!,
right: rightPhoto.cgImage!,
leftFace: &lf,
rightFace: &rf)
But the key point is that returning an UnsafeMutablePointer
is meaningless unless you have some way to ensure the lifetime of the specific thing you've taken a pointer of, which is almost never true, except within a withUnsafe...
block.