カメラ映像のズーム

OpenCvSharpでカメラ映像を出力するサンプルは調べると沢山ヒットするのですが、 カメラ映像をズームするやり方を調べても何故かヒットしないので挑戦してみました。調査したところ、 OpenCvSharpではカメラの光学ズームをプログラムから操作できないという記事を見かけましたが、 デジタルズームであればデバイスが対応していれば可能そうなので今回は下記2つの方法で作成してみました。
・カメラ映像の画像サイズを変更してデジタルズームする方法
・VideoCaptureクラスのZoomプロパティを使用する方法

開発環境はVisualStudio2019でライブラリはOpenCvSharp4.Windowsを使用しています。(バージョンは4.9.0.20240103) NuGetでインストールしてください。
C#初心者が制作したものなのでいろいろとおかしい部分が多いと思います。プログラムを流用する際は適宜修正してください。

※この記事は2024/03/04時点の情報です。

MainWindow.xaml
どちらの方法でやってもxamlは同じです。 カメラ映像を出力するImageと、Zoomの倍率を変更するSliderを配置しています。

<Window x:Class="WpfCameraCapture.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="USB Camera Capture" Width="800" Height="600"
        Loaded="Window_Loaded">
    <Grid>
        <Image x:Name="imageView" />
        <Slider Width="500" Height="50"
        TickPlacement="Both"
        Foreground="Black"
        Margin="0,0,0,0"
        IsSnapToTickEnabled="True"
        TickFrequency="1"
        Minimum="1"
        Maximum="2"
        ValueChanged="Slider_ValueChanged"/>
    </Grid>
</Window>

MainWindow.xaml.cs
1つ目の方法のソースです。 カメラ映像の画像サイズを変更してデジタルズームするやり方です。 画像の中央部分を切り取って指定サイズにリサイズし、 リサイズ後のMatをBitmapSourceに変換したものをimageコントロールに出力しています。 カメラの光学ズーム機能をプログラムから操作している訳ではありませんので、 ズームの倍率が上がるにつれて画像が荒くなります。

using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using Window = System.Windows.Window;

namespace WpfCameraCapture
{
    public partial class MainWindow : Window
    {
        private VideoCapture _capture = null;
        private double zoomFactor = 1.0;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _capture = new VideoCapture(0, VideoCaptureAPIs.DSHOW);

            if (!_capture.IsOpened())
            {
                MessageBox.Show("カメラが見つかりません。");
                Close();
                return;
            }

            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }

        private async void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            int wkWidth = 640;
            int wkHeight = 480;

            using var frame = new Mat();
            if (_capture.Read(frame))
            {
                int stX1 = (int)((wkWidth - (wkWidth / zoomFactor)) / 2);
                int widthX = (int)(wkWidth / zoomFactor);
                int stY1 = (int)((wkHeight - (wkHeight / zoomFactor)) / 2);
                int heightY = (int)(wkHeight / zoomFactor);

                using (Mat wk = new Mat())
                {
                    Cv2.Resize(frame.Clone(new OpenCvSharp.Rect(stX1, stY1, widthX, heightY)), wk, new OpenCvSharp.Size(wkWidth, wkHeight), 0, 0, InterpolationFlags.Lanczos4);
                    var bitmapSource = wk.ToBitmapSource();
                    imageView.Source = bitmapSource;
                }
            }
            else
            {
                MessageBox.Show("カメラの映像をキャプチャできません。");
                Close();
            }
            await Task.Delay(33);
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            if (_capture != null)
            {
                _capture.Dispose();
                _capture = null;
            }
        }

        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            zoomFactor = e.NewValue;
        }
    }
}

MainWindow.xaml.cs
2つ目の方法のソースです。 今回使用したOpenCvSharpのバージョンの場合、VideoCaptureクラスにはZoomプロパティが存在しており、 試しに値をセットしてみたところ、デジタルズームが動作しました。 デジタルズームに関してはカメラ側がサポートしていればZoomプロパティが有効になるようです。 残念ながら調べてもZoomプロパティに関する情報が得られなかったので参考程度にしてください。

using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using Window = System.Windows.Window;

namespace WpfCameraCapture
{
    public partial class MainWindow : Window
    {
        private VideoCapture _capture = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _capture = new VideoCapture(0, VideoCaptureAPIs.DSHOW);

            if (!_capture.IsOpened())
            {
                MessageBox.Show("カメラが見つかりません。");
                Close();
                return;
            }

            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }

        private async void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            using var frame = new Mat();
            if (_capture.Read(frame))
            {
                var bitmapSource = frame.ToBitmapSource();
                imageView.Source = bitmapSource;
            }
            else
            {
                MessageBox.Show("カメラの映像をキャプチャできません。");
                Close();
            }
            await Task.Delay(33);
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            if (_capture != null)
            {
                _capture.Dispose();
                _capture = null;
            }
        }

        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_capture != null)
            {
                _capture.Zoom = e.NewValue;
            }
        }
    }
}

Webカメラを使用するプログラムの場合、OpenCvSharpが定番のようなので いろいろとチャレンジしてみると良いかも知れませんね。

管理人情報