Created
December 4, 2022 14:05
-
-
Save lukevanin/64fdffc2f81759b48304ece4aa75c9b4 to your computer and use it in GitHub Desktop.
Returns an image with vector field lines indicating the optical flow between two images.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func opticalFlow(referenceImage: UIImage, floatingImage: UIImage) -> UIImage? { | |
let request = VNGenerateOpticalFlowRequest( | |
targetedCGImage: referenceImage.cgImage! | |
) | |
request.computationAccuracy = .high | |
request.outputPixelFormat = kCVPixelFormatType_TwoComponent32Float | |
imageRequestHandler = VNImageRequestHandler( | |
cgImage: floatingImage.cgImage! | |
) | |
try! imageRequestHandler?.perform([request]) | |
guard let results = request.results else { | |
print("No results") | |
return nil | |
} | |
guard let result = results.first else { | |
print("No result") | |
return nil | |
} | |
print("results", result) | |
let buffer = result.pixelBuffer | |
CVPixelBufferLockBaseAddress(buffer, .readOnly) | |
defer { | |
CVPixelBufferUnlockBaseAddress(buffer, .readOnly) | |
} | |
let pixelFormat = CVPixelBufferGetPixelFormatType(buffer) | |
precondition(pixelFormat == kCVPixelFormatType_TwoComponent32Float) | |
let bytesPerComponent = MemoryLayout<simd_packed_float2>.stride | |
precondition(bytesPerComponent == (4 * 2)) | |
let isPlanar = CVPixelBufferIsPlanar(buffer) | |
precondition(isPlanar == false) | |
let width = CVPixelBufferGetWidth(buffer) | |
let height = CVPixelBufferGetHeight(buffer) | |
let bytesPerRow = CVPixelBufferGetBytesPerRow(buffer) | |
let bytes = bytesPerRow * height | |
let address = CVPixelBufferGetBaseAddress(buffer)! | |
let size = CGSize(width: width, height: height) | |
let bounds = CGRect(origin: .zero, size: size) | |
let grid = 50 | |
let scale = Float32(1) | |
let backgroundColor = UIColor.black.withAlphaComponent(0.3) | |
let lineColor = UIColor.systemPink | |
let imageRenderer = UIGraphicsImageRenderer(bounds: bounds) | |
let image = imageRenderer.image { context in | |
let cgContext = context.cgContext | |
cgContext.saveGState() | |
cgContext.scaleBy(x: 1, y: -1) | |
cgContext.translateBy(x: 0, y: -bounds.height) | |
cgContext.draw(referenceImage.cgImage!, in: bounds) | |
cgContext.restoreGState() | |
cgContext.setFillColor(backgroundColor.cgColor) | |
cgContext.fill(bounds) | |
let sx = width / grid | |
let sy = height / grid | |
for y in 0 ..< sy { | |
for x in 0 ..< sx { | |
let dx = x * grid | |
let dy = y * grid | |
let origin = simd_packed_float2(x: Float32(dx), y: Float32(dy)) | |
let offset = (dy * bytesPerRow) + (dx * bytesPerComponent) | |
let pointer = address.advanced(by: offset).assumingMemoryBound(to: simd_packed_float2.self) | |
let direction = pointer.pointee | |
let vector = direction * scale | |
let length = simd_length(direction) | |
guard length < 1000 else { | |
continue | |
} | |
let start = CGPoint(origin) | |
let end = CGPoint(origin + vector) | |
cgContext.move(to: start) | |
cgContext.addLine(to: end) | |
let percent = Double((y * sx) + x) / Double(sx * sy) | |
print(String(format: "%0.3f%% %0.3f %0.3f %0.3f", percent * 100, direction.x, direction.y, length)) | |
} | |
} | |
cgContext.setLineWidth(3) | |
cgContext.setStrokeColor(lineColor.cgColor) | |
cgContext.strokePath() | |
} | |
return image | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment