You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
3.3 KiB
95 lines
3.3 KiB
//
|
|
// SCManagedCaptureDeviceSavitzkyGolayZoomHandler.m
|
|
// Snapchat
|
|
//
|
|
// Created by Yu-Kuan Lai on 4/12/17.
|
|
// Copyright © 2017 Snapchat, Inc. All rights reserved.
|
|
// https://en.wikipedia.org/wiki/Savitzky%E2%80%93Golay_filter
|
|
//
|
|
|
|
#import "SCManagedCaptureDeviceSavitzkyGolayZoomHandler.h"
|
|
|
|
#import "SCManagedCaptureDevice.h"
|
|
#import "SCManagedCaptureDeviceDefaultZoomHandler_Private.h"
|
|
|
|
#import <SCFoundation/SCQueuePerformer.h>
|
|
#import <SCFoundation/SCTraceODPCompatible.h>
|
|
|
|
static NSUInteger const kSCSavitzkyGolayWindowSize = 9;
|
|
static CGFloat const kSCUpperSharpZoomThreshold = 1.15;
|
|
|
|
@interface SCManagedCaptureDeviceSavitzkyGolayZoomHandler ()
|
|
|
|
@property (nonatomic, strong) NSMutableArray *zoomFactorHistoryArray;
|
|
|
|
@end
|
|
|
|
@implementation SCManagedCaptureDeviceSavitzkyGolayZoomHandler
|
|
|
|
- (instancetype)initWithCaptureResource:(SCCaptureResource *)captureResource
|
|
{
|
|
self = [super initWithCaptureResource:captureResource];
|
|
if (self) {
|
|
_zoomFactorHistoryArray = [[NSMutableArray alloc] init];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)setZoomFactor:(CGFloat)zoomFactor forDevice:(SCManagedCaptureDevice *)device immediately:(BOOL)immediately
|
|
{
|
|
if (self.currentDevice != device) {
|
|
// reset if device changed
|
|
self.currentDevice = device;
|
|
[self _resetZoomFactor:zoomFactor forDevice:self.currentDevice];
|
|
return;
|
|
}
|
|
|
|
if (immediately || zoomFactor == 1 || _zoomFactorHistoryArray.count == 0) {
|
|
// reset if zoomFactor is 1 or this is the first data point
|
|
[self _resetZoomFactor:zoomFactor forDevice:device];
|
|
return;
|
|
}
|
|
|
|
CGFloat lastVal = [[_zoomFactorHistoryArray lastObject] floatValue];
|
|
CGFloat upperThreshold = lastVal * kSCUpperSharpZoomThreshold;
|
|
if (zoomFactor > upperThreshold) {
|
|
// sharp change in zoomFactor, reset
|
|
[self _resetZoomFactor:zoomFactor forDevice:device];
|
|
return;
|
|
}
|
|
|
|
[_zoomFactorHistoryArray addObject:@(zoomFactor)];
|
|
if ([_zoomFactorHistoryArray count] > kSCSavitzkyGolayWindowSize) {
|
|
[_zoomFactorHistoryArray removeObjectAtIndex:0];
|
|
}
|
|
|
|
float filteredZoomFactor =
|
|
SC_CLAMP([self _savitzkyGolayFilteredZoomFactor], kSCMinVideoZoomFactor, kSCMaxVideoZoomFactor);
|
|
[self _setZoomFactor:filteredZoomFactor forManagedCaptureDevice:device];
|
|
}
|
|
|
|
- (CGFloat)_savitzkyGolayFilteredZoomFactor
|
|
{
|
|
if ([_zoomFactorHistoryArray count] == kSCSavitzkyGolayWindowSize) {
|
|
CGFloat filteredZoomFactor =
|
|
59 * [_zoomFactorHistoryArray[4] floatValue] +
|
|
54 * ([_zoomFactorHistoryArray[3] floatValue] + [_zoomFactorHistoryArray[5] floatValue]) +
|
|
39 * ([_zoomFactorHistoryArray[2] floatValue] + [_zoomFactorHistoryArray[6] floatValue]) +
|
|
14 * ([_zoomFactorHistoryArray[1] floatValue] + [_zoomFactorHistoryArray[7] floatValue]) -
|
|
21 * ([_zoomFactorHistoryArray[0] floatValue] + [_zoomFactorHistoryArray[8] floatValue]);
|
|
filteredZoomFactor /= 231;
|
|
return filteredZoomFactor;
|
|
} else {
|
|
return [[_zoomFactorHistoryArray lastObject] floatValue]; // use zoomFactor directly if we have less than 9
|
|
}
|
|
}
|
|
|
|
- (void)_resetZoomFactor:(CGFloat)zoomFactor forDevice:(SCManagedCaptureDevice *)device
|
|
{
|
|
[_zoomFactorHistoryArray removeAllObjects];
|
|
[_zoomFactorHistoryArray addObject:@(zoomFactor)];
|
|
[self _setZoomFactor:zoomFactor forManagedCaptureDevice:device];
|
|
}
|
|
|
|
@end
|