旅行好きなソフトエンジニアの備忘録

プログラミングや技術関連のメモを始めました

【WPF】 Imageを拡大/平行移動させる

表示している画像をマウスのホイールを使って拡大したり、ドラッグして平行移動させたいと思ったところ、以下の回答が評価が高かったです。

stackoverflow.com

ただ、リンクの通りに実装するとTransformGroupへキャストする時にキャストエラーが出て動作させることが出来ませんでした。でもリンク先を少し変更すると動作したのでメモしておきます。

まず、xaml側はこんな感じです。

<Border Name="Border1" ClipToBounds="True">
    <Image Name="Image1" MouseWheel="Image1_MouseWheel" MouseLeftButtonDown="Image1_MouseLeftButtonDown" MouseMove="Image1_MouseMove" MouseLeftButtonUp="Image1_MouseLeftButtonUp"/>
</Border>


次に拡大するためのロジックは以下のようになります。マウスホイールを回すことで、今マウスのある位置で拡大することができます。

private void Image1_MouseWheel(object sender, MouseWheelEventArgs e)
{
    // スケールの値を変えることでホイールを動かした時の拡大率を制御できます
    const double scale = 1.2;

    var matrix = Image1.RenderTransform.Value;
    if (e.Delta > 0)
    {
        // 拡大処理
        matrix.ScaleAt(scale, scale, e.GetPosition(this).X, e.GetPosition(this).Y);
    }
    else
    {
        // 縮小処理
        matrix.ScaleAt(1.0/scale, 1.0/scale, e.GetPosition(this).X, e.GetPosition(this).Y);
    }

    Image1.RenderTransform = new MatrixTransform(matrix);
}

f:id:ni4muraano:20171021134748p:plain:w250f:id:ni4muraano:20171021134758p:plain:w250
次にドラッグして平行移動させるロジックは以下になります。これにより、左クリックを押したままドラッグすることで画像を平行移動できます。

private Point _start;

private void Image1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    Image1.CaptureMouse();
    _start = e.GetPosition(Border1);
}

private void Image1_MouseMove(object sender, MouseEventArgs e)
{
    if (Image1.IsMouseCaptured)
    {
        var matrix = Image1.RenderTransform.Value;

        Vector v = _start - e.GetPosition(Border1);
        matrix.Translate(-v.X, -v.Y);
        Image1.RenderTransform = new MatrixTransform(matrix);
        _start = e.GetPosition(Border1);
    }
}

private void Image1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    Image1.ReleaseMouseCapture();
}

f:id:ni4muraano:20171021135234p:plain:w250f:id:ni4muraano:20171021135239p:plain:w250
最後に以下を加えるとEscキーを押した時に拡大/縮小、平行移動した画像を元に戻せます。

private void Window_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Escape)
    {
        var matrix = Image1.RenderTransform.Value;
        matrix.M11 = 1.0;
        matrix.M12 = 0.0;
        matrix.M21 = 0.0;
        matrix.M22 = 1.0;
        matrix.OffsetX = 0.0;
        matrix.OffsetY = 0.0;
        Image1.RenderTransform = new MatrixTransform(matrix);
    }
}

【OpenCVSharp】 ヒートマップを作成する

OpenCVSharpを使ってヒートマップを描くプログラムのメモです。ヒートマップは赤⇒緑⇒青の順で遷移するため、HSV空間を使い、その後RGB空間に戻すと簡単に作成できます(OpenCVでは0が赤、60が緑、120が青に相当します)。

// 画像サイズ
const int size = 101;
// ガウス関数のシグマ
const double sigma = 20.0;
// HSVを記録する行列
var hsvMap = new Mat(new Size(size, size), MatType.CV_8UC3);
for (int i = -50; i <= 50; ++i)
{
    for (int j = -50; j <= 50; ++j)
    {
        // ガウス関数の値
        double g = Math.Exp(-(Math.Pow(i, 2) + Math.Pow(j, 2))/(2.0*Math.Pow(sigma, 2)));
        // 色相の計算(OpenCVでは0で赤、120で青)
        byte hue = (byte)(120*(1.0 - g));
        hsvMap.Set(i+50, j+50, new Vec3b(hue, 255, 255));
    }
}
// HSVをBGRに変換する
var bgrMap = new Mat(new Size(size, size), MatType.CV_8UC3);
Cv2.CvtColor(hsvMap, bgrMap, ColorConversionCodes.HSV2BGR);
// ヒートマップの保存
bgrMap.ImWrite("heatmap.bmp");
hsvMap.Dispose();
bgrMap.Dispose();

上記で出来たヒートマップが以下の写真になります。
f:id:ni4muraano:20171018215712j:plain

【WPF】 ボタンのサイズをリソースで指定する

作っているアプリケーションにボタンが沢山あるのですが、それらの幅、高さは全て共通なのでハードコーディングではなく一括管理したいと考えました。そのやり方ですが、以下の2ステップになります。

① App.xamlに以下を追加(xmlns:sysと<sys:Double ...>の三行を追加してます)

<Application x:Class="ResourcePractice.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ResourcePractice"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <sys:Double x:Key="ButtonWidth">100</sys:Double>
        <sys:Double x:Key="ButtonHeight">100</sys:Double>
    </Application.Resources>
</Application>


② Buttonから先程のリソースを参照する

<Button Width="{StaticResource ButtonWidth}" Height="{StaticResource ButtonHeight}"/>


ちなみにこのやり方は以下のリンクを参考にしました。

stackoverflow.com

【WPF】 続・スクリーンショットを撮り続けて動画に保存する

以前スクリーンショットを撮り続けて動画に保存する記事を書いたのですが、実はMicrosoft Expression Encoderを使えばやりたいことは出来たようです。 ni4muraano.hatenablog.com

Microsoft Expression Encoderはここからダウンロードできます。NuGetからもダウンロードできるのですが、動作させようとすると良く分からないエラーが出て結局動かすことができませんでした。結局自分は下記手順で動作させることができました。

  1. リンクからMicrosoft Expression Encoderをダウンロード・インストールして
  2. NuGetからMicrosoft Expression Encoderをインストールする

プログラムについてですが、まずスクリーンショットを撮り続けて動画に保存する部分は以下になります(System.Drawing.Rectangleを使うためにSystem.Drawingを参照に追加してください)。

// usingに追加
using Microsoft.Expression.Encoder;
using Microsoft.Expression.Encoder.ScreenCapture;

// フィールドでキャプチャーを宣言
private readonly ScreenCaptureJob _capture = new ScreenCaptureJob();

// 設定、キャプチャー開始命令
_capture.CaptureRectangle = new System.Drawing.Rectangle(0, 0, 1200, 940);
_capture.ScreenCaptureVideoProfile.FrameRate = 30;
_capture.OutputScreenCaptureFileName = @"C:\Users\MyFolder\Videos\video.xesc";
_capture.Start();

そして、以下を実行することで保存した動画をWMVにエンコーディングできます。

// Stop, Disposeしないと動画が作成されない
_capture.Stop();
_capture.Dispose();
// 作成した動画をエンコーディングする
var media = new MediaItem(@"C:\Users\MyFolder\Videos\video.xesc"); ;
var job = new Job();
job.MediaItems.Add(media);
job.OutputDirectory = @"C:\Users\MyFolder\Videos";
job.Encode();

【WPF】 BitmapSourceクラスをbitmapとして保存する

BitmapSourceクラスをbitmapとして保存する方法を探していたのですが、以下に書かれていたのでメモします。

stackoverflow.com

public void SaveBitmapSourceToFile(BitmapSource bitmapSource, string filePath)
{
    using (var fileStream = new FileStream(filePath, FileMode.Create))
    {
        BitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
        encoder.Save(fileStream);
    }
}

【WPF】 UIElement上のマウスイベントが発生しない

Imageの上にCanvasを置いて、CanvasにLineやEllipseをマウス操作を通じて描くことで、仮想的にImage上に絵を描いたように見えるようにしたいと思っています。

<Image .../>
<Canvas .../>

そこでCanvasにMouseDownイベントを付けたのですが、Canvasをクリックしても一向にMouseDownイベントが発生しませんでした。このような時の対処法がここに書かれていました。

自分の場合はリンク先に書かれている1番のやり方(CanvasのBackgroundにTransparentを指定すること)でMouseDownイベントが発生するようになりました。