사진 및 비디오 캡처를 위한 수동 카메라 컨트롤

이 문서는 수동 디바이스 컨트롤을 사용하여 광학 이미지 손떨림 보정 및 부드러운 확대/축소를 비롯한 향상된 사진 및 비디오 캡처 시나리오를 사용하도록 설정하는 방법을 보여줍니다.

이 문서에서 설명하는 컨트롤은 모두 동일한 패턴을 사용하여 앱에 추가됩니다. 먼저 검사 앱이 실행 중인 현재 디바이스에서 컨트롤이 지원되는지 확인합니다. 컨트롤이 지원되는 경우 컨트롤에 대해 원하는 모드를 설정합니다. 일반적으로 특정 컨트롤이 현재 디바이스에서 지원되지 않는 경우 사용자가 기능을 사용하도록 설정할 수 있는 UI 요소를 사용하지 않도록 설정하거나 숨겨야 합니다.

이 문서의 코드는 수동 카메라 컨트롤 SDK 샘플에서 조정되었습니다. 샘플을 다운로드하여 컨텍스트에서 사용된 코드를 보거나 사용자 고유의 앱의 시작점으로 사용할 수 있습니다.

참고 항목

이 문서는 기본 사진 및 비디오 캡처 구현 단계를 설명하는 MediaCapture를 사용한 기본적인 사진, 비디오 및 오디오 캡처에 설명된 개념 및 코드를 토대로 작성되었습니다. 좀 더 수준 높은 캡처 시나리오를 진행하기 전에 해당 문서의 기본 미디어 캡처 패턴을 좀 더 잘 이해하는 것이 좋습니다. 이 문서의 코드는 앱에 적절히 초기화된 MediaCapture의 인스턴스가 이미 있다고 가정합니다.

이 문서에서 설명하는 모든 디바이스 제어 API는 Windows.Media.Devices 네임스페이스의 멤버입니다.

using Windows.Media.Devices;

노출

ExposureControl을 사용하면 사진 또는 비디오 캡처 중에 사용되는 셔터 속도를 설정할 수 있습니다.

이 예제에서는 슬라이더 컨트롤을 사용하여 현재 노출 값을 조정하고 확인란를 사용하여 자동 노출 조정을 전환합니다.

<Slider Name="ExposureSlider" ValueChanged="ExposureSlider_ValueChanged"/>
<TextBlock Name="ExposureTextBlock" Text="{Binding ElementName=ExposureSlider,Path=Value}"/>
<CheckBox Name="ExposureAutoCheckBox" Content="Auto" Checked="ExposureCheckBox_CheckedChanged" Unchecked="ExposureCheckBox_CheckedChanged"/>

현재 캡처 디바이스가 Supported 속성을 검사 ExposureControl을 지원하는지 확인합니다. 컨트롤이 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다. 자동 노출 조정이 현재 Auto 속성 값으로 활성화되어 있는지 여부를 나타내려면 확인란의 검사 상태를 설정합니다.

노출 값은 디바이스에서 지원하는 범위 내에 있어야 하며 지원되는 단계 크기의 증분이어야 합니다. 슬라이더 컨트롤의 해당 속성을 설정하는 데 사용되는 최소, 최대, 및 단계속성을 검사 현재 디바이스에 대해 지원되는 값을 가져옵니다.

값이 설정될 때 이벤트가 트리거되지 않도록 ValueChanged 이벤트 처리기를 등록 취소한 후 슬라이더 컨트롤의 값을 ExposureControl의 현재 값으로 설정합니다.

var exposureControl = _mediaCapture.VideoDeviceController.ExposureControl;

if (exposureControl.Supported)
{
    ExposureAutoCheckBox.Visibility = Visibility.Visible;
    ExposureSlider.Visibility = Visibility.Visible;

    ExposureAutoCheckBox.IsChecked = exposureControl.Auto;

    ExposureSlider.Minimum = exposureControl.Min.Ticks;
    ExposureSlider.Maximum = exposureControl.Max.Ticks;
    ExposureSlider.StepFrequency = exposureControl.Step.Ticks;

    ExposureSlider.ValueChanged -= ExposureSlider_ValueChanged;
    var value = exposureControl.Value;
    ExposureSlider.Value = value.Ticks;
    ExposureSlider.ValueChanged += ExposureSlider_ValueChanged;
}
else
{
    ExposureAutoCheckBox.Visibility = Visibility.Collapsed;
    ExposureSlider.Visibility = Visibility.Collapsed;
}

ValueChanged 이벤트 처리기에서 SetValueAsync를 호출하여 컨트롤의 현재 값을 가져오고 노출 값을 설정합니다.

private async void ExposureSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
    var value = TimeSpan.FromTicks((long)(sender as Slider).Value);
    await _mediaCapture.VideoDeviceController.ExposureControl.SetValueAsync(value);
}

자동 노출 검사 상자의 CheckedChanged 이벤트 처리기에서 SetAutoAsync를 호출하고 부울 값을 전달하여 자동 노출 조정을 켜거나 끕니다.

private async void ExposureCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
{
    if(! _isPreviewing)
    {
        // Auto exposure only supported while preview stream is running.
        return;
    }

    var autoExposure = ((sender as CheckBox).IsChecked == true);
    await _mediaCapture.VideoDeviceController.ExposureControl.SetAutoAsync(autoExposure);
}

Important

자동 노출 모드는 미리 보기 스트림이 실행되는 동안에만 지원됩니다. 자동 노출을 켜기 전에 미리 보기 스트림이 실행 중인지 확인합니다.

노출 보정

ExposureCompensationControl을 사용하면 사진 또는 비디오 캡처 중에 사용되는 노출 보정을 설정할 수 있습니다.

다음은 Slider 컨트롤을 사용하여 현재 노출 보정 값을 조정하는 예제입니다.

<Slider Name="EvSlider" ValueChanged="EvSlider_ValueChanged"/>
<TextBlock Text="{Binding ElementName=EvSlider,Path=Value}" Name="EvTextBlock"/>

Supported 속성을 확인하여 현재 캡처 디바이스가 ExposureCompensationControl을 지원하는지 확인합니다. 컨트롤이 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다.

노출 보정 값은 디바이스에서 지원하는 범위 내에 있어야 하며 지원되는 단계 크기의 증분이어야 합니다. 슬라이더 컨트롤의 해당 속성을 설정하는 데 사용되는 최소, 최대, 및 단계속성을 검사 현재 디바이스에 대해 지원되는 값을 가져옵니다.

값이 설정될 때 이벤트가 트리거되지 않도록 ValueChanged 이벤트 처리기를 등록 취소한 후 슬라이더 컨트롤의 값을 ExposureCompensationControl의 현재 값으로 설정합니다.

var exposureCompensationControl = _mediaCapture.VideoDeviceController.ExposureCompensationControl;

if (exposureCompensationControl.Supported)
{
    EvSlider.Visibility = Visibility.Visible;
    EvSlider.Minimum = exposureCompensationControl.Min;
    EvSlider.Maximum = exposureCompensationControl.Max;
    EvSlider.StepFrequency = exposureCompensationControl.Step;

    EvSlider.ValueChanged -= EvSlider_ValueChanged;
    EvSlider.Value = exposureCompensationControl.Value;
    EvSlider.ValueChanged += EvSlider_ValueChanged;
}
else
{
    EvSlider.Visibility = Visibility.Collapsed;
}

ValueChanged 이벤트 처리기에서 SetValueAsync를 호출하여 컨트롤의 현재 값을 가져오고 노출 값을 설정합니다.

private async void EvSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
    var value = (sender as Slider).Value;
    await _mediaCapture.VideoDeviceController.ExposureCompensationControl.SetValueAsync((float)value);
}

Flash

FlashControl을 사용하면 플래시를 사용하거나 사용하지 않도록 설정하거나 자동 플래시를 사용하도록 설정할 수 있습니다. 여기서 시스템은 플래시를 사용할지 여부를 동적으로 결정합니다. 또한 이 컨트롤을 사용하면 이를 지원하는 디바이스에서 자동 적목 현상 감소를 사용하도록 설정할 수 있습니다. 이러한 설정은 모두 사진 캡처에 적용됩니다. TorchControl은 비디오 캡처를 위해 토치를 켜거나 끄기 위한 별도의 컨트롤입니다.

이 예제에서는 라디오 단추 집합을 사용하여 사용자가 켜기, 끄기 및 자동 플래시 설정 간에 전환할 수 있도록 합니다. 검사 상자도 제공되어 적목 현상 감소 및 비디오 토치를 전환할 수 있습니다.

<RadioButton Name="FlashOnRadioButton" Content="On" Checked="FlashOnRadioButton_Checked"/>
<RadioButton Name="FlashAutoRadioButton" Content="Auto" Checked="FlashAutoRadioButton_Checked"/>
<RadioButton Name="FlashOffRadioButton" Content="Off" Checked="FlashOffRadioButton_Checked"/>
<CheckBox Name="RedEyeFlashCheckBox" Content="Red Eye" Visibility="Collapsed" Checked="RedEyeFlashCheckBox_CheckedChanged" Unchecked="RedEyeFlashCheckBox_CheckedChanged"/>
<CheckBox Name="TorchCheckBox" Content="Video Light" Visibility="Collapsed" Checked="TorchCheckBox_CheckedChanged" Unchecked="TorchCheckBox_CheckedChanged"/>

현재 캡처 디바이스가 Supported 속성을 검사 FlashControl을 지원하는지 확인합니다. 컨트롤이 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다. FlashControl이 지원되는 경우 자동 적목 현상 감소가 지원되거나 지원되지 않을 수 있으므로 UI를 사용하도록 설정하기 전에 RedEyeReductionSupported 속성을 검사하세요. TorchControl은 플래시 컨트롤과 분리되어 있으므로 사용 전에 지원되는 속성도 검사합니다.

각 플래시 라디오 단추에 대한 Checked 이벤트 처리기에서 적절한 해당 플래시 설정을 사용하거나 사용하지 않도록 설정합니다. 플래시를 항상 사용하도록 설정하려면 Enabled 속성을 true로 설정하고 Auto 속성을 false로 설정해야 합니다.

var flashControl = _mediaCapture.VideoDeviceController.FlashControl;

if (flashControl.Supported)
{
    FlashAutoRadioButton.Visibility = Visibility.Visible;
    FlashOnRadioButton.Visibility = Visibility.Visible;
    FlashOffRadioButton.Visibility = Visibility.Visible;

    FlashAutoRadioButton.IsChecked = true;

    if (flashControl.RedEyeReductionSupported)
    {
        RedEyeFlashCheckBox.Visibility = Visibility.Visible;
    }

    // Video light is not strictly part of flash, but users might expect to find it there
    if (_mediaCapture.VideoDeviceController.TorchControl.Supported)
    {
        TorchCheckBox.Visibility = Visibility.Visible;
    }
}
else
{
    FlashAutoRadioButton.Visibility = Visibility.Collapsed;
    FlashOnRadioButton.Visibility = Visibility.Collapsed;
    FlashOffRadioButton.Visibility = Visibility.Collapsed;
}
private void FlashOnRadioButton_Checked(object sender, RoutedEventArgs e)
{
    _mediaCapture.VideoDeviceController.FlashControl.Enabled = true;
    _mediaCapture.VideoDeviceController.FlashControl.Auto = false;
}

private void FlashAutoRadioButton_Checked(object sender, RoutedEventArgs e)
{
    _mediaCapture.VideoDeviceController.FlashControl.Enabled = true;
    _mediaCapture.VideoDeviceController.FlashControl.Auto = true;
}

private void FlashOffRadioButton_Checked(object sender, RoutedEventArgs e)
{
    _mediaCapture.VideoDeviceController.FlashControl.Enabled = false;
}

적목 현상 감소 검사 상자에 대한 처리기에서 RedEyeReduction 속성을 적절한 값으로 설정합니다.

private void RedEyeFlashCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
{
    _mediaCapture.VideoDeviceController.FlashControl.RedEyeReduction = (RedEyeFlashCheckBox.IsChecked == true);
}

마지막으로 비디오 토치 검사 상자에 대한 처리기에서 Enabled 속성을 적절한 값으로 설정합니다.

private void TorchCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
{
    _mediaCapture.VideoDeviceController.TorchControl.Enabled = (TorchCheckBox.IsChecked == true);

    if(! (_isPreviewing && _isRecording))
    {
        System.Diagnostics.Debug.WriteLine("Torch may not emit light if preview and video capture are not running.");
    }
}

참고 항목

일부 디바이스에서는 디바이스가 미리 보기 스트림을 실행하고 비디오를 적극적으로 캡처하지 않는 한 TorchControl.Enabled가 true로 설정된 경우에도 토치가 빛을 내보내지 않습니다. 권장되는 작업 순서는 비디오 미리 보기를 켜고 Enabled를 true로 설정하여 토치를 켜고 비디오 캡처를 시작하는 것입니다. 일부 디바이스에서는 미리 보기가 시작된 후 토치가 켜집니다. 다른 장치에서는 비디오 캡처가 시작될 때까지 토치가 켜지지 않을 수 있습니다.

포커스

카메라의 포커스를 조정하기 위해 일반적으로 사용되는 세 가지 방법은 FocusControl 개체, 연속 자동 초점, 포커스 탭 및 수동 포커스에서 지원됩니다. 카메라 앱은 이러한 세 가지 방법을 모두 지원할 수 있지만 가독성을 위해 이 문서에서는 각 기술에 대해 개별적으로 설명합니다. 이 섹션에서는 포커스 보조 조명을 사용하도록 설정하는 방법에 대해서도 설명합니다.

연속 자동 초점

연속 자동 초점을 사용하도록 설정하면 카메라에서 포커스를 동적으로 조정하여 사진 또는 비디오의 제목을 포커스에 유지하도록 합니다. 이 예제에서는 라디오 단추를 사용하여 연속 자동 포커스를 켜고 끕니다.

<RadioButton Content="CAF" Name="CafFocusRadioButton" Checked="CafFocusRadioButton_Checked"/>

현재 캡처 디바이스가 FocusControl 속성을 검사 FlashControl을 지원하는지 확인합니다. 다음으로, SupportedFocusModes 목록을 검사 연속 자동 포커스가 지원되는지 확인하여 FocusMode.Continuous 값이 포함되어 있는지 확인하고, 있는 경우 연속 자동 초점 라디오 단추를 표시합니다.

var focusControl = _mediaCapture.VideoDeviceController.FocusControl;

if (focusControl.Supported)
{
    CafFocusRadioButton.Visibility = focusControl.SupportedFocusModes.Contains(FocusMode.Continuous) 
        ? Visibility.Visible : Visibility.Collapsed;
}
else
{
    CafFocusRadioButton.Visibility = Visibility.Collapsed;
}

연속 자동 초점 라디오 단추에 대한 Checked 이벤트 처리기에서 VideoDeviceController.FocusControl 속성을 사용하여 컨트롤의 인스턴스를 가져옵니다. 앱이 이전에 LockAsync를 호출하여 다른 포커스 모드 중 하나를 사용하도록 설정한 경우 UnlockAsync를 호출하여 컨트롤의 잠금을 해제합니다.

FocusSettings 개체를 만들고 Mode 속성을 Continuous로 설정합니다. AutoFocusRange 속성을 앱 시나리오에 적합한 값으로 설정하거나 UI에서 사용자가 선택합니다. FocusSettings 개체를 Configure 메서드에 전달한 다음 FocusAsync를 호출하여 연속 자동 초점을 시작합니다.

private async void CafFocusRadioButton_Checked(object sender, RoutedEventArgs e)
{
    if(! _isPreviewing)
    {
        // Autofocus only supported while preview stream is running.
        return;
    }

    var focusControl = _mediaCapture.VideoDeviceController.FocusControl;
    await focusControl.UnlockAsync();
    var settings = new FocusSettings { Mode = FocusMode.Continuous, AutoFocusRange = AutoFocusRange.FullRange };
    focusControl.Configure(settings);
    await focusControl.FocusAsync();
}

Important

자동 초점 모드는 미리 보기 스트림이 실행되는 동안에만 지원됩니다. 연속 자동 초점을 켜기 전에 미리 보기 스트림이 실행 중인지 확인합니다.

탭하여 포커스

포커스 탭 기술은 FocusControlRegionsOfInterestControl을 사용하여 캡처 디바이스가 집중해야 하는 캡처 프레임의 하위 리소스를 지정합니다. 포커스 영역은 미리 보기 스트림을 표시하는 화면을 탭하는 사용자에 의해 결정됩니다.

이 예제에서는 라디오 단추를 사용하여 탭-포커스 모드를 사용하거나 사용하지 않도록 설정합니다.

<RadioButton Content="Tap" Name="TapFocusRadioButton" Checked="TapFocusRadioButton_Checked"/>

현재 캡처 디바이스가 FocusControl 속성을 검사 FlashControl을 지원하는지 확인합니다. 이 기술을 사용하려면 RegionsOfInterestControl 이 지원되어야 하며 하나 이상의 지역을 지원해야 합니다. AutoFocusSupportedMaxRegions 속성을 확인하여 탭-포커스에 대한 라디오 단추를 표시하거나 숨길지 여부를 확인합니다.

var focusControl = _mediaCapture.VideoDeviceController.FocusControl;

if (focusControl.Supported)
{
    TapFocusRadioButton.Visibility = (_mediaCapture.VideoDeviceController.RegionsOfInterestControl.AutoFocusSupported &&
                                      _mediaCapture.VideoDeviceController.RegionsOfInterestControl.MaxRegions > 0) 
                                      ? Visibility.Visible : Visibility.Collapsed;
}
else
{
    TapFocusRadioButton.Visibility = Visibility.Collapsed;
}

탭-포커스 단추에 대한 Checked 이벤트 처리기에서 VideoDeviceController.FocusControl 속성을 사용하여 컨트롤의 인스턴스를 가져옵니다. LockAsync를 호출하여 앱이 이전에 UnlockAsync를 호출하여 연속 자동 초점을 사용하도록 설정한 경우 컨트롤을 잠그고 사용자가 화면을 탭하여 포커스를 변경할 때까지 기다립니다.

private async void TapFocusRadioButton_Checked(object sender, RoutedEventArgs e)
{
    // Lock focus in case Continuous Autofocus was active when switching to Tap-to-focus
    var focusControl = _mediaCapture.VideoDeviceController.FocusControl;
    await focusControl.LockAsync();
    // Wait for user tap
}

다음은 사용자가 화면을 탭할 때 영역에 초점을 맞춘 다음 사용자가 토글처럼 다시 탭할 때 해당 영역에서 포커스를 제거하는 예제입니다. 부울 변수를 사용하여 현재 토글된 상태를 추적합니다.

bool _isFocused = false;

다음 단계는 사용자가 현재 캡처 미리 보기 스트림을 표시하고 있는 CaptureElementTapped 이벤트를 처리하여 화면을 탭할 때 이벤트를 수신 대기하는 것입니다. 카메라가 현재 미리 보기로 표시되지 않거나 탭-포커스 모드가 비활성화된 경우 아무 작업도 수행하지 않고 처리기에서 반환합니다.

추적 변수 _isFocused가 false로 전환되고 카메라가 현재 초점 프로세스( FocusControlFocusState 속성에 의해 결정)에 있지 않은 경우 탭하여 초점 맞추기 프로세스를 시작합니다. 처리기에 전달된 이벤트 인수에서 사용자의 탭 위치를 가져옵니다. 이 예제에서는 이 기회를 사용하여 초점을 맞출 지역의 크기를 선택합니다. 이 경우 크기는 캡처 요소의 가장 작은 차원의 1/4입니다. 탭 위치와 지역 크기를 다음 섹션에 정의된 TapToFocus 도우미 메서드에 전달합니다.

_isFocused 토글이 true로 설정된 경우 사용자가 탭하면 이전 영역의 초점이 지워집니다. 이 작업은 아래 표시된 TapUnfocus 도우미 메서드에서 수행됩니다.

private async void PreviewControl_Tapped(object sender, TappedRoutedEventArgs e)
{
    if (!_isPreviewing || (TapFocusRadioButton.IsChecked != true)) return;

    if (!_isFocused && _mediaCapture.VideoDeviceController.FocusControl.FocusState != MediaCaptureFocusState.Searching)
    {
        var smallEdge = Math.Min(Window.Current.Bounds.Width, Window.Current.Bounds.Height);

        // Choose to make the focus rectangle 1/4th the length of the shortest edge of the window
        var size = new Size(smallEdge / 4, smallEdge / 4);
        var position = e.GetPosition(sender as UIElement);

        // Note that at this point, a rect at "position" with size "size" could extend beyond the preview area. The following method will reposition the rect if that is the case
        await TapToFocus(position, size);
    }
    else
    {
        await TapUnfocus();
    }
}

TapToFocus 도우미 메서드에서 먼저 _isFocused 토글을 true로 설정하여 다음 화면 탭이 탭한 영역의 초점을 해제하도록 합니다.

이 도우미 메서드의 다음 작업은 포커스 컨트롤에 할당될 미리 보기 스트림 내의 사각형을 확인하는 것입니다. 이렇게 하려면 두 단계가 필요합니다. 첫 번째 단계는 미리 보기 스트림이 CaptureElement 컨트롤 내에서 차지하는 사각형을 확인하는 것입니다. 이는 미리 보기 스트림의 차원과 디바이스의 방향에 따라 달라집니다. 이 섹션의 끝에 표시된 도우미 메서드 GetPreviewStreamRectInControl은 이 작업을 수행하고 미리 보기 스트림이 포함된 사각형을 반환합니다.

TapToFocus의 다음 작업은 CaptureElement.Tapped 이벤트 처리기 내에서 결정된 탭 위치와 원하는 포커스 사각형 크기를 캡처 스트림 내의 좌표로 변환하는 것입니다. 이 섹션의 뒷부분에 표시된 ConvertUiTapToPreviewRect 도우미 메서드는 이 변환을 수행하고 포커스가 요청되는 캡처 스트림 좌표에서 사각형을 반환합니다.

이제 대상 사각형을 얻었으므로 새 RegionOfInterest 개체를 만들고, Bounds 속성을 이전 단계에서 가져온 대상 사각형으로 설정합니다.

캡처 디바이스의 FocusControl을 가져옵니다. 새 FocusSettings 개체를 만들고 검사 후 ModeAutoFocusRange를 원하는 값으로 설정하여 FocusControl에서 지원되는지 확인합니다. FocusControl에서 Configure을 호출하여 설정을 활성화하고 디바이스가 지정된 지역에 집중하기 시작하도록 알릴 수 있습니다.

다음으로 캡처 디바이스의 RegionsOfInterestControl을 가져와 SetRegionsAsync를 호출하여 활성 지역을 설정합니다. 관심 있는 여러 지역을 지원하는 디바이스에서 설정할 수 있지만 이 예제에서는 단일 지역만 설정합니다.

마지막으로 FocusControl에서 FocusAsync를 호출하여 포커스를 시작합니다.

Important

탭을 구현하여 포커스를 맞추는 경우 작업 순서가 중요합니다. 다음 순서대로 이러한 API를 호출해야 합니다.

  1. FocusControl.Configure
  2. RegionsOfInterestControl.SetRegionsAsync
  3. FocusControl.FocusAsync
public async Task TapToFocus(Point position, Size size)
{
    _isFocused = true;

    var previewRect = GetPreviewStreamRectInControl();
    var focusPreview = ConvertUiTapToPreviewRect(position, size, previewRect);

    // Note that this Region Of Interest could be configured to also calculate exposure 
    // and white balance within the region
    var regionOfInterest = new RegionOfInterest
    {
        AutoFocusEnabled = true,
        BoundsNormalized = true,
        Bounds = focusPreview,
        Type = RegionOfInterestType.Unknown,
        Weight = 100,
    };


    var focusControl = _mediaCapture.VideoDeviceController.FocusControl;
    var focusRange = focusControl.SupportedFocusRanges.Contains(AutoFocusRange.FullRange) ? AutoFocusRange.FullRange : focusControl.SupportedFocusRanges.FirstOrDefault();
    var focusMode = focusControl.SupportedFocusModes.Contains(FocusMode.Single) ? FocusMode.Single : focusControl.SupportedFocusModes.FirstOrDefault();
    var settings = new FocusSettings { Mode = focusMode, AutoFocusRange = focusRange };
    focusControl.Configure(settings);

    var roiControl = _mediaCapture.VideoDeviceController.RegionsOfInterestControl;
    await roiControl.SetRegionsAsync(new[] { regionOfInterest }, true);

    await focusControl.FocusAsync();
}

TapUnfocus 도우미 메서드에서 RegionsOfInterestControl을 가져오고 ClearRegionsAsync를 호출하여 TapToFocus 도우미 메서드 내에서 컨트롤에 등록된 지역을 지웁니다. 그런 다음 FocusControl을 가져와 FocusAsync를 호출하여 관심 영역 없이 디바이스가 다시 초점을 맞추도록 합니다.

private async Task TapUnfocus()
{
    _isFocused = false;

    var roiControl = _mediaCapture.VideoDeviceController.RegionsOfInterestControl;
    await roiControl.ClearRegionsAsync();

    var focusControl = _mediaCapture.VideoDeviceController.FocusControl;
    await focusControl.FocusAsync();
}

GetPreviewStreamRectInControl 도우미 메서드는 미리 보기 스트림의 해상도와 디바이스의 방향을 사용하여 미리 보기 스트림이 포함된 미리 보기 요소 내의 사각형을 결정하고 컨트롤이 스트림의 가로 세로 비율을 기본 위해 제공할 수 있는 레터박스 패딩을 잘라낸다. 이 메서드는 MediaCapture를 사용하여 기본 사진, 비디오 및 오디오 캡처에 있는 기본 미디어 캡처 예제 코드에 정의된 클래스 멤버 변수를 사용합니다.

public Rect GetPreviewStreamRectInControl()
{
    var result = new Rect();

    var previewResolution = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;

    // In case this function is called before everything is initialized correctly, return an empty result
    if (PreviewControl == null || PreviewControl.ActualHeight < 1 || PreviewControl.ActualWidth < 1 ||
        previewResolution == null || previewResolution.Height == 0 || previewResolution.Width == 0)
    {
        return result;
    }

    var streamWidth = previewResolution.Width;
    var streamHeight = previewResolution.Height;

    // For portrait orientations, the width and height need to be swapped
    if (_displayOrientation == DisplayOrientations.Portrait || _displayOrientation == DisplayOrientations.PortraitFlipped)
    {
        streamWidth = previewResolution.Height;
        streamHeight = previewResolution.Width;
    }

    // Start by assuming the preview display area in the control spans the entire width and height both (this is corrected in the next if for the necessary dimension)
    result.Width = PreviewControl.ActualWidth;
    result.Height = PreviewControl.ActualHeight;

    // If UI is "wider" than preview, letterboxing will be on the sides
    if ((PreviewControl.ActualWidth / PreviewControl.ActualHeight > streamWidth / (double)streamHeight))
    {
        var scale = PreviewControl.ActualHeight / streamHeight;
        var scaledWidth = streamWidth * scale;

        result.X = (PreviewControl.ActualWidth - scaledWidth) / 2.0;
        result.Width = scaledWidth;
    }
    else // Preview stream is "wider" than UI, so letterboxing will be on the top+bottom
    {
        var scale = PreviewControl.ActualWidth / streamWidth;
        var scaledHeight = streamHeight * scale;

        result.Y = (PreviewControl.ActualHeight - scaledHeight) / 2.0;
        result.Height = scaledHeight;
    }

    return result;
}

ConvertUiTapToPreviewRect 도우미 메서드는 탭 이벤트의 위치, 포커스 영역의 원하는 크기 및 GetPreviewStreamRectInControl 도우미 메서드에서 가져온 미리 보기 스트림을 포함하는 사각형을 인수로 사용합니다. 이 메서드는 이러한 값과 디바이스의 현재 방향을 사용하여 원하는 지역을 포함하는 미리 보기 스트림 내의 사각형을 계산합니다. 다시 한 번, 이 메서드는 MediaCapture를 사용하여 사진 및 비디오 캡처에 있는 기본 미디어 캡처 예제 코드에 정의된 클래스 멤버 변수를 사용합니다.

private Rect ConvertUiTapToPreviewRect(Point tap, Size size, Rect previewRect)
{
    // Adjust for the resulting focus rectangle to be centered around the position
    double left = tap.X - size.Width / 2, top = tap.Y - size.Height / 2;

    // Get the information about the active preview area within the CaptureElement (in case it's letterboxed)
    double previewWidth = previewRect.Width, previewHeight = previewRect.Height;
    double previewLeft = previewRect.Left, previewTop = previewRect.Top;

    // Transform the left and top of the tap to account for rotation
    switch (_displayOrientation)
    {
        case DisplayOrientations.Portrait:
            var tempLeft = left;

            left = top;
            top = previewRect.Width - tempLeft;
            break;
        case DisplayOrientations.LandscapeFlipped:
            left = previewRect.Width - left;
            top = previewRect.Height - top;
            break;
        case DisplayOrientations.PortraitFlipped:
            var tempTop = top;

            top = left;
            left = previewRect.Width - tempTop;
            break;
    }

    // For portrait orientations, the information about the active preview area needs to be rotated
    if (_displayOrientation == DisplayOrientations.Portrait || _displayOrientation == DisplayOrientations.PortraitFlipped)
    {
        previewWidth = previewRect.Height;
        previewHeight = previewRect.Width;
        previewLeft = previewRect.Top;
        previewTop = previewRect.Left;
    }

    // Normalize width and height of the focus rectangle
    var width = size.Width / previewWidth;
    var height = size.Height / previewHeight;

    // Shift rect left and top to be relative to just the active preview area
    left -= previewLeft;
    top -= previewTop;

    // Normalize left and top
    left /= previewWidth;
    top /= previewHeight;

    // Ensure rectangle is fully contained within the active preview area horizontally
    left = Math.Max(left, 0);
    left = Math.Min(1 - width, left);

    // Ensure rectangle is fully contained within the active preview area vertically
    top = Math.Max(top, 0);
    top = Math.Min(1 - height, top);

    // Create and return resulting rectangle
    return new Rect(left, top, width, height);
}

수동 포커스

수동 포커스 기술은 슬라이더 컨트롤을 사용하여 캡처 디바이스의 현재 포커스 깊이를 설정합니다. 라디오 단추는 수동 포커스를 켜고 끄는 데 사용됩니다.

<Slider Name="FocusSlider" IsEnabled="{Binding ElementName=ManualFocusRadioButton,Path=IsChecked}" ValueChanged="FocusSlider_ValueChanged"/>
<TextBlock Text="{Binding ElementName=FocusSlider,Path=Value,FallbackValue='0'}"/>
<RadioButton Content="Manual" Name="ManualFocusRadioButton" Checked="ManualFocusRadioButton_Checked" IsChecked="False"/>

현재 캡처 디바이스가 FocusControl 속성을 검사 FlashControl을 지원하는지 확인합니다. 컨트롤이 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다.

포커스 값은 디바이스에서 지원하는 범위 내에 있어야 하며 지원되는 단계 크기의 증분이어야 합니다. 슬라이더 컨트롤의 해당 속성을 설정하는 데 사용되는 최소, 최대, 및 단계속성을 검사 현재 디바이스에 대해 지원되는 값을 가져옵니다.

값이 설정될 때 이벤트가 트리거되지 않도록 ValueChanged 이벤트 처리기를 등록 취소한 후 슬라이더 컨트롤의 값을 FocusControl의 현재 값으로 설정합니다.

var focusControl = _mediaCapture.VideoDeviceController.FocusControl;

if (focusControl.Supported)
{
    FocusSlider.Visibility = Visibility.Visible;
    ManualFocusRadioButton.Visibility = Visibility.Visible;

    FocusSlider.Minimum = focusControl.Min;
    FocusSlider.Maximum = focusControl.Max;
    FocusSlider.StepFrequency = focusControl.Step;
    

    FocusSlider.ValueChanged -= FocusSlider_ValueChanged;
    FocusSlider.Value = focusControl.Value;
    FocusSlider.ValueChanged += FocusSlider_ValueChanged;
}
else
{
    FocusSlider.Visibility = Visibility.Collapsed;
    ManualFocusRadioButton.Visibility = Visibility.Collapsed;
}

수동 포커스 라디오 단추에 대한 Checked 이벤트 처리기에서 FocusControl 개체를 가져오고 앱이 이전에 UnlockAsync를 호출하여 포커스를 잠금 해제한 경우 LockAsync를 호출합니다.

private async void ManualFocusRadioButton_Checked(object sender, RoutedEventArgs e)
{
    var focusControl = _mediaCapture.VideoDeviceController.FocusControl;
    await focusControl.LockAsync();
}

수동 포커스 슬라이더의 ValueChanged 이벤트 처리기에서 SetValueAsync를 호출하여 컨트롤의 현재 값을 가져오고 포커스 값을 설정합니다.

private async void FocusSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
    var value = (sender as Slider).Value;
    await _mediaCapture.VideoDeviceController.FocusControl.SetValueAsync((uint)value);
}

포커스 표시등 사용

이를 지원하는 디바이스에서 포커스 보조 조명을 사용하도록 설정하여 디바이스 포커스를 지원할 수 있습니다. 이 예제에서는 확인란을 사용하여 포커스 보조 조명을 사용하거나 사용하지 않도록 설정합니다.

<CheckBox Content="Assist Light" Name="FocusLightCheckBox" IsEnabled="{Binding ElementName=TapFocusRadioButton,Path=IsChecked}"
                  Checked="FocusLightCheckBox_CheckedChanged" Unchecked="FocusLightCheckBox_CheckedChanged"/>

현재 캡처 디바이스가 Supported 속성을 검사 FlashControl을 지원하는지 확인합니다. 또한 AssistantLightSupported를 검사하여 보조 조명도 지원되는지 확인합니다. 이 두 가지가 모두 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다.

var focusControl = _mediaCapture.VideoDeviceController.FocusControl;

if (focusControl.Supported)
{

    FocusLightCheckBox.Visibility = (_mediaCapture.VideoDeviceController.FlashControl.Supported &&
                                     _mediaCapture.VideoDeviceController.FlashControl.AssistantLightSupported) ? Visibility.Visible : Visibility.Collapsed;
}
else
{
    FocusLightCheckBox.Visibility = Visibility.Collapsed;
}

CheckedChanged 이벤트 처리기에서 캡처 디바이스 FlashControl 개체를 가져옵니다. AssistantLightEnabled 속성을 설정하여 포커스 표시등을 사용하거나 사용하지 않도록 설정합니다.

private void FocusLightCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
{
    var flashControl = _mediaCapture.VideoDeviceController.FlashControl;

    flashControl.AssistantLightEnabled = (FocusLightCheckBox.IsChecked == true);
}

ISO 속도

IsoSpeedControl을 사용하면 사진 또는 비디오 캡처 중에 사용되는 ISO 속도를 설정할 수 있습니다.

이 예제에서는 슬라이더 컨트롤을 사용하여 현재 노출 보정 값을 조정하고 확인란를 사용하여 ISO 속도 조정을 전환합니다.

<Slider Name="IsoSlider" ValueChanged="IsoSlider_ValueChanged"/>
<TextBlock Text="{Binding ElementName=IsoSlider,Path=Value}" Visibility="{Binding ElementName=IsoSlider,Path=Visibility}"/>
<CheckBox Name="IsoAutoCheckBox" Content="Auto" Checked="IsoAutoCheckBox_CheckedChanged" Unchecked="IsoAutoCheckBox_CheckedChanged"/>

현재 캡처 디바이스가 Supported 속성을 검사 IsoSpeedControl을 지원하는지 확인합니다. 컨트롤이 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다. ISO 속도 조정이 현재 Auto 속성 값으로 활성화되어 있는지 여부를 나타내려면 확인란의 검사 상태를 설정합니다.

ISO 속도 값은 디바이스에서 지원하는 범위 내에 있어야 하며 지원되는 단계 크기의 증분이어야 합니다. 슬라이더 컨트롤의 해당 속성을 설정하는 데 사용되는 최소, 최대, 및 단계속성을 검사 현재 디바이스에 대해 지원되는 값을 가져옵니다.

값이 설정될 때 이벤트가 트리거되지 않도록 ValueChanged 이벤트 처리기를 등록 취소한 후 슬라이더 컨트롤의 값을 IsoSpeedControl의 현재 값으로 설정합니다.

private void UpdateIsoControlCapabilities()
{
    var isoSpeedControl = _mediaCapture.VideoDeviceController.IsoSpeedControl;

    if (isoSpeedControl.Supported)
    {
        IsoAutoCheckBox.Visibility = Visibility.Visible;
        IsoSlider.Visibility = Visibility.Visible;

        IsoAutoCheckBox.IsChecked = isoSpeedControl.Auto;
        
        IsoSlider.Minimum = isoSpeedControl.Min;
        IsoSlider.Maximum = isoSpeedControl.Max;
        IsoSlider.StepFrequency = isoSpeedControl.Step;

        IsoSlider.ValueChanged -= IsoSlider_ValueChanged;
        IsoSlider.Value = isoSpeedControl.Value;
        IsoSlider.ValueChanged += IsoSlider_ValueChanged;
    }
    else
    {
        IsoAutoCheckBox.Visibility = Visibility.Collapsed;
        IsoSlider.Visibility = Visibility.Collapsed;
    }
}

ValueChanged 이벤트 처리기에서 SetValueAsync를 호출하여 컨트롤의 현재 값을 가져오고 ISO 속도 값을 설정합니다.

private async void IsoSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
    var value = (sender as Slider).Value;
    await _mediaCapture.VideoDeviceController.IsoSpeedControl.SetValueAsync((uint)value);
}

자동 ISO 속도 검사box의 CheckedChanged 이벤트 처리기에서 SetAutoAsync를 호출하여 자동 ISO 속도 조정을 켭니다. SetValueAsync를 호출하고 슬라이더 컨트롤의 현재 값을 전달하여 자동 ISO 속도 조정을 해제합니다.

private async void IsoAutoCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
{
    var autoIso = (sender as CheckBox).IsChecked == true;

    if (autoIso)
    {
        await _mediaCapture.VideoDeviceController.IsoSpeedControl.SetAutoAsync();
    }
    else
    {
        await _mediaCapture.VideoDeviceController.IsoSpeedControl.SetValueAsync((uint)IsoSlider.Value);
    }
}

광학 이미지 손떨림 보정

OIS(광학 이미지 손떨림 보정)는 하드웨어 캡처 장치를 기계적으로 조작하여 캡처된 비디오 스트림을 안정화시켜 디지털 안정화보다 뛰어난 결과를 제공합니다. OIS를 지원하지 않는 디바이스에서는 VideoStabilizationEffect를 사용하여 캡처된 바이드에서 디지털 안정화를 수행할 수 있습니다. 자세한 내용은 비디오 캡처에 대한 효과를 참조하세요.

OpticalImageStabilizationControl.Supported 속성을 검사하여 현재 디바이스에서 OIS가 지원되는지 확인합니다.

OIS 컨트롤은 켜기, 끄기 및 자동의 세 가지 모드를 지원합니다. 즉, 디바이스는 OIS가 미디어 캡처를 개선할지 여부를 동적으로 결정하고, 그렇다면 OIS를 사용하도록 설정합니다. 디바이스에서 특정 모드가 지원되는지 확인하려면 검사 OpticalImageStabilizationControl.SupportedModes 컬렉션에 원하는 모드가 포함되어 있는지 확인합니다.

OpticalImageStabilizationControl.Mode를 원하는 모드로 설정하여 OIS를 사용하거나 사용하지 않도록 설정합니다.

private void SetOpticalImageStabilizationMode(OpticalImageStabilizationMode mode)
{
    if (!_mediaCapture.VideoDeviceController.OpticalImageStabilizationControl.Supported)
    {
        ShowMessageToUser("Optical image stabilization not available");
        return;
    }

    var stabilizationModes = _mediaCapture.VideoDeviceController.OpticalImageStabilizationControl.SupportedModes;

    if (!stabilizationModes.Contains(mode))
    {
        ShowMessageToUser("Optical image stabilization setting not supported");
        return;
    }

    _mediaCapture.VideoDeviceController.OpticalImageStabilizationControl.Mode = mode;
}

전력선 빈도

일부 카메라 디바이스는 현재 환경에서 전력선의 AC 주파수를 아는 데 따라 달라지는 깜박임 방지 처리를 지원합니다. 일부 디바이스는 전력선 주파수의 자동 결정을 지원하는 반면, 다른 디바이스는 주파수를 수동으로 설정해야 합니다. 다음 코드 예제에서는 디바이스에서 전력선 주파수 지원을 확인하는 방법과 필요한 경우 빈도를 수동으로 설정하는 방법을 보여 줍니다.

먼저 VideoDeviceController 메서드 TryGetPowerlineFrequency를 호출하여 PowerlineFrequency 형식의 출력 매개 변수를 전달합니다. 이 호출이 실패하면 현재 디바이스에서 전력선 주파수 제어가 지원되지 않습니다. 기능이 지원되는 경우 자동 모드를 설정하여 디바이스에서 자동 모드를 사용할 수 있는지 확인할 수 있습니다. TrySetPowerlineFrequency를 호출하고 Auto 값을 전달하여 이 작업을 수행합니다. 호출이 성공하면 자동 powerline 주파수가 지원됩니다. 디바이스에서 전원선 주파수 컨트롤러가 지원되지만 자동 주파수 검색이 지원되지 않는 경우 TrySetPowerlineFrequency를 사용하여 빈도를 수동으로 설정할 수 있습니다. 이 예제에서 MyCustomFrequencyLookup은 디바이스의 현재 위치에 대한 올바른 빈도를 결정하기 위해 구현하는 사용자 지정 메서드입니다.

 PowerlineFrequency getFrequency;

 if (! _mediaCapture.VideoDeviceController.TryGetPowerlineFrequency(out getFrequency))
 {
     // Powerline frequency is not supported on this device.
     return;
 }

 if (! _mediaCapture.VideoDeviceController.TrySetPowerlineFrequency(PowerlineFrequency.Auto))
 {
     // Set the frequency manually
     PowerlineFrequency setFrequency = MyCustomFrequencyLookup();
     if (_mediaCapture.VideoDeviceController.TrySetPowerlineFrequency(setFrequency))
     {
         System.Diagnostics.Debug.WriteLine(String.Format("Powerline frequency manually set to {0}.", setFrequency));
     }
 }

흰색 균형

WhiteBalanceControl을 사용하면 사진 또는 비디오 캡처 중에 사용되는 화이트 밸런스를 설정할 수 있습니다.

이 예제에서는 ComboBox 컨트롤을 사용하여 기본 제공 색 온도 미리 설정에서 선택하고 수동 흰색 균형 조정을 위해 슬라이더 컨트롤을 선택합니다.

<Slider Name="WbSlider" ValueChanged="WbSlider_ValueChanged"/>
<TextBlock Name="WbTextBox" Text="{Binding ElementName=WbSlider,Path=Value}" Visibility="{Binding ElementName=WbSlider,Path=Visibility}"/>
<ComboBox Name="WbComboBox" SelectionChanged="WbComboBox_SelectionChanged"/>

현재 캡처 디바이스가 Supported 속성을 검사 WhiteBalanceControll을 지원하는지 확인합니다. 컨트롤이 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다. 콤보 상자의 항목을 ColorTemperaturePreset 열거형 값으로 설정합니다. 선택한 항목을 Preset 속성의 현재 값으로 설정합니다.

수동 제어의 경우 화이트 밸런스 값은 디바이스에서 지원하는 범위 내에 있어야 하며 지원되는 단계 크기의 증분이어야 합니다. 슬라이더 컨트롤의 해당 속성을 설정하는 데 사용되는 최소, 최대, 및 단계속성을 검사 현재 디바이스에 대해 지원되는 값을 가져옵니다. 수동 제어를 사용하도록 설정하기 전에 검사 지원되는 최소값과 최대값 사이의 범위가 단계 크기보다 큰지 확인합니다. 그렇지 않으면 현재 디바이스에서 수동 제어가 지원되지 않습니다.

값이 설정될 때 이벤트가 트리거되지 않도록 ValueChanged 이벤트 처리기를 등록 취소한 후 슬라이더 컨트롤의 값을 WhiteBalanceControl의 현재 값으로 설정합니다.

           var whiteBalanceControl = _mediaCapture.VideoDeviceController.WhiteBalanceControl;

           if (whiteBalanceControl.Supported)
           {
               WbSlider.Visibility = Visibility.Visible;
               WbComboBox.Visibility = Visibility.Visible;

               if (WbComboBox.ItemsSource == null)
               {
                   WbComboBox.ItemsSource = Enum.GetValues(typeof(ColorTemperaturePreset)).Cast<ColorTemperaturePreset>();
               }

               WbComboBox.SelectedItem = whiteBalanceControl.Preset;

               if (whiteBalanceControl.Max - whiteBalanceControl.Min > whiteBalanceControl.Step)
               {

                   WbSlider.Minimum = whiteBalanceControl.Min;
                   WbSlider.Maximum = whiteBalanceControl.Max;
                   WbSlider.StepFrequency = whiteBalanceControl.Step;

                   WbSlider.ValueChanged -= WbSlider_ValueChanged;
                   WbSlider.Value = whiteBalanceControl.Value;
                   WbSlider.ValueChanged += WbSlider_ValueChanged;
               }
               else
               {
                   WbSlider.Visibility = Visibility.Collapsed;
               }
           }
           else
           {
               WbSlider.Visibility = Visibility.Collapsed;
               WbComboBox.Visibility = Visibility.Collapsed;
           }

색 온도 사전 설정 콤보 상자의 SelectionChanged 이벤트 처리기에서 현재 선택한 사전 설정을 가져오고 SetPresetAsync를 호출하여 컨트롤의 값을 설정합니다. 선택한 미리 설정된 값이 수동이 아니면 수동 화이트 밸런스 슬라이더를 사용하지 않도록 설정합니다.

private async void WbComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if(!_isPreviewing)
    {
        // Do not set white balance values unless the preview stream is running.
        return;
    }

    var selected = (ColorTemperaturePreset)WbComboBox.SelectedItem;
    WbSlider.IsEnabled = (selected == ColorTemperaturePreset.Manual);
    await _mediaCapture.VideoDeviceController.WhiteBalanceControl.SetPresetAsync(selected);

}

ValueChanged 이벤트 처리기에서 SetValueAsync를 호출하여 컨트롤의 현재 값을 가져오고 화이트 밸런스 값을 설정합니다.

private async void WbSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
    if (!_isPreviewing)
    {
        // Do not set white balance values unless the preview stream is running.
        return;
    }

    var value = (sender as Slider).Value;
    await _mediaCapture.VideoDeviceController.WhiteBalanceControl.SetValueAsync((uint)value);
}

Important

화이트 밸런스 조정은 미리 보기 스트림이 실행되는 동안에만 지원됩니다. 화이트 밸런스 값 또는 미리 설정을 설정하기 전에 미리 보기 스트림이 실행 중인지 확인합니다.

Important

ColorTemperaturePreset.Auto 사전 설정 값은 시스템에 화이트 밸런스 수준을 자동으로 조정하도록 지시합니다. 각 프레임에 대해 흰색 균형 수준이 동일해야 하는 사진 시퀀스 캡처와 같은 일부 시나리오의 경우 컨트롤을 현재 자동 값으로 잠글 수 있습니다. 이렇게 하려면 SetPresetAsync를 호출하고 수동 사전 설정을 지정하고 SetValueAsync를 사용하여 컨트롤에 값을 설정하지 않습니다. 이로 인해 디바이스가 현재 값을 잠급 수 있습니다. 이 값이 올바르지 않으므로 현재 컨트롤 값을 읽은 다음 반환된 값을 SetValueAsync에 전달하지 마세요.

확대/축소

ZoomControl을 사용하면 사진 또는 비디오 캡처 중에 사용되는 확대/축소 수준을 설정할 수 있습니다.

다음은 슬라이더 컨트롤을 사용하여 현재 확대/축소 수준을 조정하는 예제입니다. 다음 섹션에서는 화면의 손가락 모으기 제스처에 따라 확대/축소를 조정하는 방법을 보여 있습니다.

<Slider Name="ZoomSlider" Grid.Row="0" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Stretch" ValueChanged="ZoomSlider_ValueChanged"/>
<TextBlock Grid.Row="1" HorizontalAlignment="Center" Text="{Binding ElementName=ZoomSlider,Path=Value}"/>

현재 캡처 디바이스가 Supported 속성을 검사 ZoomControl을 지원하는지 확인합니다. 컨트롤이 지원되는 경우 이 기능에 대해 UI를 표시하고 사용하도록 설정할 수 있습니다.

확대/축소 수준 값은 디바이스에서 지원하는 범위 내에 있어야 하며 지원되는 단계 크기의 증분이어야 합니다. 슬라이더 컨트롤의 해당 속성을 설정하는 데 사용되는 최소, 최대, 및 단계속성을 검사 현재 디바이스에 대해 지원되는 값을 가져옵니다.

값이 설정될 때 이벤트가 트리거되지 않도록 ValueChanged 이벤트 처리기를 등록 취소한 후 슬라이더 컨트롤의 값을 ZoomControl의 현재 값으로 설정합니다.

var zoomControl = _mediaCapture.VideoDeviceController.ZoomControl;

if (zoomControl.Supported)
{
    ZoomSlider.Visibility = Visibility.Visible;

    ZoomSlider.Minimum = zoomControl.Min;
    ZoomSlider.Maximum = zoomControl.Max;
    ZoomSlider.StepFrequency = zoomControl.Step;
    
    ZoomSlider.ValueChanged -= ZoomSlider_ValueChanged;
    ZoomSlider.Value = zoomControl.Value;
    ZoomSlider.ValueChanged += ZoomSlider_ValueChanged;
}
else
{
    ZoomSlider.Visibility = Visibility.Collapsed;
}

ValueChanged 이벤트 처리기에서 ZoomSettings 클래스의 새 인스턴스를 만들고 Value 속성을 확대/축소 슬라이더 컨트롤의 현재 값으로 설정합니다. ZoomControlSupportedModes 속성에 ZoomTransitionMode.Smooth가 포함되어 있으면 디바이스가 확대/축소 수준 간의 원활한 전환을 지원한다는 의미입니다. 이 모드는 더 나은 사용자 환경을 제공하므로 일반적으로 ZoomSettings 개체의 Mode속성에 이 값을 사용하려고 합니다.

마지막으로 ZoomControl 개체의 Configure 메서드에 ZoomSettings 개체를 전달하여 현재 확대/축소 설정을 변경합니다.

private void ZoomSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
    var level = (float)ZoomSlider.Value;
    var settings = new ZoomSettings { Value = level };

    var zoomControl = _mediaCapture.VideoDeviceController.ZoomControl;
    if (zoomControl.SupportedModes.Contains(ZoomTransitionMode.Smooth))
    {
        settings.Mode = ZoomTransitionMode.Smooth;
    }
    else
    {
        settings.Mode = zoomControl.SupportedModes.First();
    }

    zoomControl.Configure(settings);
}

손가락 모으기 제스처를 사용하여 부드러운 확대/축소

이전 섹션에서 설명한 것처럼 이를 지원하는 디바이스에서 부드러운 확대/축소 모드를 사용하면 캡처 디바이스가 디지털 확대/축소 수준 간에 원활하게 전환될 수 있으므로 사용자가 개별 전환 및 장애 전환 없이 캡처 작업 중에 확대/축소 수준을 동적으로 조정할 수 있습니다. 이 섹션에서는 손가락 모으기 제스처에 대한 응답으로 확대/축소 수준을 조정하는 방법을 설명합니다.

먼저 ZoomControl.Supported 속성을 검사하여 디지털 확대/축소 컨트롤이 현재 디바이스에서 지원되는지 확인합니다. 그런 다음 ZoomControl.SupportedModes를 검사하여 부드러운 확대/축소 모드를 사용할 수 있는지 확인하여 ZoomTransitionMode.Smooth 값이 포함되어 있는지 확인합니다.

private bool IsSmoothZoomSupported()
{
    if (!_mediaCapture.VideoDeviceController.ZoomControl.Supported)
    {
        ShowMessageToUser("Digital zoom is not supported on this device.");
        return false;
    }

    var zoomModes = _mediaCapture.VideoDeviceController.ZoomControl.SupportedModes;

    if (!zoomModes.Contains(ZoomTransitionMode.Smooth))
    {
        ShowMessageToUser("Smooth zoom not supported");
        return false;
    }

    return true;
}

멀티 터치 사용 디바이스에서 일반적인 시나리오는 두 손가락 모으기 제스처에 따라 확대/축소 요소를 조정하는 것입니다. CaptureElement 컨트롤의 ManipulationMode 속성을 ManipulationModes.Scale로 설정하여 손가락 모으기 제스처를 사용하도록 설정합니다. 그런 다음, 손가락 모으기 제스처의 크기가 변경될 때 발생하는 ManipulationDelta 이벤트에 등록합니다.

private void RegisterPinchGestureHandler()
{
    if (!IsSmoothZoomSupported())
    {
        return;
    }

    // Enable pinch/zoom gesture for the preview control
    PreviewControl.ManipulationMode = ManipulationModes.Scale;
    PreviewControl.ManipulationDelta += PreviewControl_ManipulationDelta;
}

ManipulationDelta 이벤트에 대한 처리기에서 사용자의 손가락 모으기 제스처의 변경 내용에 따라 확대/축소 요소를 업데이트합니다. ManipulationDelta.Scale 값은 핀치 크기의 작은 증가가 1.0보다 약간 큰 숫자이고, 핀치 크기의 작은 감소가 1.0보다 약간 작은 숫자가 되도록 핀치 제스처의 배율 변화를 나타냅니다. 이 예제에서는 확대/축소 컨트롤의 현재 값에 배율 델타를 곱합니다.

확대/축소 인수를 설정하기 전에 값이 ZoomControl.Min 속성에 표시된 대로 디바이스에서 지원하는 최소값보다 작지 않은지 확인해야 합니다. 또한 값이 ZoomControl.Max 값보다 작거나 같은지 확인합니다. 마지막으로 확대/축소 배율은 디바이스에서 지원되는 확대/축소 단계 크기(Step 속성으로 지정)의 배수여야 합니다. 확대/축소 요소가 이러한 요구 사항을 충족하지 않으면 캡처 디바이스에서 확대/축소 수준을 설정하려고 할 때 예외가 throw됩니다.

ZoomSettings 개체를 만들어 캡처 디바이스의 확대/축소 수준을 설정합니다. Mode 속성을 ZoomTransitionMode.Smooth로 설정한 다음 Value 속성을 원하는 확대/축소 요소로 설정합니다. 마지막으로 ZoomControl.Configure를 호출하여 디바이스에서 새 확대/축소 값을 설정합니다. 디바이스는 새 확대/축소 값으로 원활하게 전환됩니다.

private void PreviewControl_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
    var zoomControl = _mediaCapture.VideoDeviceController.ZoomControl;

    // Example zoom factor calculation based on size of scale gesture
    var zoomFactor = zoomControl.Value * e.Delta.Scale;

    if (zoomFactor < zoomControl.Min) zoomFactor = zoomControl.Min;
    if (zoomFactor > zoomControl.Max) zoomFactor = zoomControl.Max;
    zoomFactor = zoomFactor - (zoomFactor % zoomControl.Step);

    var settings = new ZoomSettings();
    settings.Mode = ZoomTransitionMode.Smooth;
    settings.Value = zoomFactor;

    _mediaCapture.VideoDeviceController.ZoomControl.Configure(settings);

}