Skip to content

Commit cd60853

Browse files
author
Chris Greening
committed
Adds averaging over multiple frames
1 parent 6ce4217 commit cd60853

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

IrProCapture/Camera/Camera.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class Camera: NSObject, ObservableObject, CaptureDelegate {
6565

6666
// Private components
6767
private let ciContext = CIContext()
68-
private let temperatureProcessor = TemperatureProcessor()
68+
private let temperatureProcessor = TemperatureProcessor(averagingEnabled: true, maxFrameCount: 2)
6969
private let videoRecorder = VideoRecorder()
7070
private var isProcessing = false
7171
private var capture: Capture?

IrProCapture/Camera/Components/TemperatureProcessor.swift

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,47 @@ class TemperatureProcessor {
4949
/// Height of the thermal sensor in pixels
5050
private let height: Int = 192
5151

52+
/// Buffer storing recent frames for averaging
53+
private var frameBuffer: [[Float]] = []
54+
/// Maximum number of frames to average
55+
private var maxFrameCount: Int = 5
56+
/// Whether frame averaging is enabled
57+
private var averagingEnabled: Bool = false
58+
59+
/// Initializes a new TemperatureProcessor.
60+
///
61+
/// - Parameters:
62+
/// - averagingEnabled: Whether frame averaging is enabled (default: false)
63+
/// - maxFrameCount: Maximum number of frames to keep for averaging (default: 5)
64+
init(averagingEnabled: Bool = false, maxFrameCount: Int = 5) {
65+
self.averagingEnabled = averagingEnabled
66+
self.maxFrameCount = max(1, maxFrameCount)
67+
}
68+
69+
/// Enables or disables frame averaging.
70+
///
71+
/// - Parameter enabled: Whether averaging should be enabled
72+
func setAveragingEnabled(_ enabled: Bool) {
73+
if averagingEnabled != enabled {
74+
frameBuffer.removeAll()
75+
averagingEnabled = enabled
76+
}
77+
}
78+
79+
/// Sets the number of frames to use for averaging.
80+
///
81+
/// - Parameter count: Number of frames to average (minimum 1)
82+
func setFrameCount(_ count: Int) {
83+
let newCount = max(1, count)
84+
if maxFrameCount != newCount {
85+
maxFrameCount = newCount
86+
// Trim buffer if needed
87+
while frameBuffer.count > maxFrameCount {
88+
frameBuffer.removeFirst()
89+
}
90+
}
91+
}
92+
5293
/// Computes a histogram of temperature values.
5394
///
5495
/// - Parameters:
@@ -81,6 +122,7 @@ class TemperatureProcessor {
81122
/// 2. Converts UInt16 values to floating-point temperatures
82123
/// 3. Applies calibration formula to get actual temperatures
83124
/// 4. Computes various statistics and histogram data
125+
/// 5. If enabled, averages with previous frames to reduce noise
84126
///
85127
/// - Parameters:
86128
/// - buffer: Raw camera data buffer
@@ -109,6 +151,34 @@ class TemperatureProcessor {
109151
let scale: Float = 1.0 / 64.0
110152
let offset: Float = -273.2
111153
temperatures = vDSP.add(offset, vDSP.multiply(scale, temperatures))
154+
155+
// Step 4: Apply frame averaging if enabled
156+
if averagingEnabled {
157+
// Add current frame to buffer
158+
frameBuffer.append(temperatures)
159+
160+
// Keep buffer at correct size
161+
while frameBuffer.count > maxFrameCount {
162+
frameBuffer.removeFirst()
163+
}
164+
165+
// Average frames if we have more than one
166+
if frameBuffer.count > 1 {
167+
var averagedTemperatures = [Float](repeating: 0, count: width * height)
168+
169+
// Sum all frames
170+
for frame in frameBuffer {
171+
vDSP.add(averagedTemperatures, frame, result: &averagedTemperatures)
172+
}
173+
174+
// Divide by frame count
175+
let scale = 1.0 / Float(frameBuffer.count)
176+
vDSP.multiply(scale, averagedTemperatures, result: &averagedTemperatures)
177+
178+
// Use averaged temperatures for statistics
179+
temperatures = averagedTemperatures
180+
}
181+
}
112182

113183
// Calculate statistics
114184
let minValue = temperatures.min() ?? 0.0

0 commit comments

Comments
 (0)