OpenCvSharpを使ってH264動画をWEBカメラから撮る

NuGetでインストール:
OpenCvSharp3-AnyCPU

アプリケーションExeファイルと同じフォルダに必要:
openh264-1.8.0-win64.dll

※上記2者の相性が良くてうまく機能しているが、逆に最新版はダメ。

 

using System;
using System.ComponentModel;
using System.Windows.Forms;
using OpenCvSharp;

namespace DirectShowLib_Demo
{
  public partial class Form1 : Form
  {
    const int CONST_VIDEO_WIDTH = 1280;
    const int CONST_VIDEO_HEIGHT = 700;
    const int CONST_VIDEO_RRAMERATE = 30;
    int useVideoWidth;
    int useVideoHeight;
    VideoWriter video;
    int cannelIndex = 0;
    string filePath = "";

    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      // DoWorkイベントハンドラの実行を開始
      worker.RunWorkerAsync();

      aviPlayer.uiMode = "none";
      aviPlayer.windowlessVideo = false;
      aviPlayer.settings.autoStart = true;
      aviPlayer.enableContextMenu = false;
      aviPlayer.stretchToFit = true;
      aviPlayer.settings.setMode("loop", false);

      timer1.Enabled = true;
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
      try
      {
        // 動画ファイルを閉じる
        if (video != null && !video.IsDisposed)
        {
          video.Dispose();
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }

    // 動画撮影ボタン押下処理
    private void button2_Click(object sender, EventArgs e)
    {
      try
      {
        if (label2.Text == "STOP")
        {
          label2.Text = "REC";
          filePath = $@"C:\Temp\video_{DateTime.Now.ToLongTimeString().Replace(":", "")}.mp4";
          video = new VideoWriter(
              filePath,
              FourCC.H264,
              30,
              new OpenCvSharp.Size(useVideoWidth, useVideoHeight));

        }
        else if (label2.Text == "REC")
        {
          label2.Text = "STOP";

          // 動画ファイルを閉じる
          video.Dispose();
          aviPlayer.URL = filePath;
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }

    // バックグラウンドワーカーの作業処理
    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
      try
      {
        BackgroundWorker worker = (BackgroundWorker)sender;

        while (true)
        {
          VideoCapture cap = new VideoCapture(cannelIndex);
          int curCaneelIndex = cannelIndex;

          cap.Set(CaptureProperty.FrameWidth, CONST_VIDEO_WIDTH);
          cap.Set(CaptureProperty.FrameHeight, CONST_VIDEO_HEIGHT);

          useVideoWidth = cap.FrameWidth;
          useVideoHeight = cap.FrameHeight;

          if (cap.IsOpened())
          {
            while (true)
            {
              if(curCaneelIndex == cannelIndex)
              {
                var frame = cap.RetrieveMat();
                worker.ReportProgress(0, frame);
              }
              else
              {
                cap.Dispose();
                break;
              }
            }
          }
          else
          {
            MessageBox.Show("Error. There is no Camera!!!");
            this.Close();
          }
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }

    // バックグラウンドワーカーの状態変化処理
    private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
      try
      {
        // frameがe.UserStateプロパティにセットされて渡されてくる
        Mat image = (Mat)e.UserState;

        if (label2.Text == "REC")
        {
          // 動画ファイルに書き込み
          video.Write(image);
        }
        pictureBoxIpl1.ImageIpl = image;
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }

    //動画ループ再生時ちらつき対策
    private void timer1_Tick(object sender, EventArgs e)
    {
      if(aviPlayer.Ctlcontrols.currentItem != null)
      {
        if (aviPlayer.Ctlcontrols.currentPosition > aviPlayer.Ctlcontrols.currentItem.duration - 0.01)
        {
          aviPlayer.Ctlcontrols.currentPosition = 0;
        }
      }
    }
  }
}

パネルからコントロールを正しく削除する方法

動的にコントロールの追加と削除を繰り返していたら、「ウィンドウのハンドルを作成中にエラーが発生しました。」という例外が発生した。

削除は以下のように書いていた。

panel1.Controls.Clear();

コントロールのハンドルを解放するには以下のように書くべき。

for(int i = panel1.Controls.Count - 1; 0 <= i; i--) {
panel1.Controls[i].Dispose();
}

※後ろから解放している点が重要らしい。

オラクルDBサーバに接続時Timeoutエラーの解決

C:>sqlplus mie_user/imspass@192.168.0.12:1521
以下のエラーが表示されました。
ORA-12170: TNS:Connect timeout occurred

原因:
オラクルDBサーバのファイアウォールが有効になってポート1521が遮断されたからでした。

解決方法:
オラクルDBサーバのファイヤーウォールを無効にしました。(一時対応)

フォントサイズをポイントからピクセルに変更

Form_Paint或いはPanelコントロールの~_Paintメソッドの中に、PaintEventArgs型のパラメータeからディスプレイのDPIを取得できる。
横方向のDPI : e.Graphics.DpiX
縦方向のDPI : e.Graphics.DpiY

例えば、1920*1080のディスプレイを100%に設定した場合は96で、125%設定の場合は120となる。(DpiXとDpiXが同じの場合がほとんど)

① 縦方向のDPIを取得して保存しておく。

  Utility.DpiY = e.Graphics.DpiY

② 幾何変換のための3×3擬似空間行列クラスのインスタンスを作る。

  System.Drawing.Drawing2D.Matrix m = CurrentDicomImage.Matrix(CurrentDicomViewer);

③ フォントサイズをポイント単位からピクセル単位に変換する。

  short FontSizeInPixel = (short)(FontSizeInPoint / 72.0 * Utility.DpiY / DicomGlobal.Zoom(m));

※1Pointは1/72Inchと相当する。

※上記はDicomImage空間の数値を得る。

IISでのFTPバインド設定について

IISFTPのバインド設置でIPアドレスを「*」に設定すると、localhost, 127.0.0.1, 192.168.0.xxxのどれでもftpでログインできる。

逆に「192.168.0.123」だけバインドすると、このIPだけftpできるようになる。