simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

画像処理メモ

画像処理の顔検出技術において
高速化するうえで便利な技法に積分画像というものが知られています。

Haar-like 特徴量とかSURFというこれまた有名な局所特徴量記述形式などにも利用され
コンピュータビジョンを仕事にしている人で、知らない人はいないほど(趣味でやっている人は知らないかも)

何がうれしいって
例えば画像の平均輝度を計算したいとき、すべての画素の値を参照して合計輝度値を出し、合計ピクセル数で割ればよいではないですか
でも、もし画像領域を十字の境界線を引いて4等分に割って、それぞれの領域の平均輝度を計算したいときがあった場合
もう一回、すべての画素値を参照して計算します?
なんだか無駄に計算時間を使っていそうですね

そう

この処理、積分画像を使うととても少ない計算量で同じ答えが出せるようになります。

どうやってやるか、理解するかはここが参考になります。

Webcam画像を表示

EWCLIBヘルプ
というものを使いました。

下記コード(これだけではビルドが通りませんが…)の要領で Webcam 画像を表示してみました。

// OpenCVCameraViewer.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
/**
* @file Threshold.cpp
* @brief Sample code that shows how to use the diverse threshold options offered by OpenCV
* @author OpenCV team
*/

#include "opencv2/highgui/highgui.hpp"
#include "ewclib/ewclib.h"

using namespace cv;
const char* window_name = "Threshold Demo";

/**
* @function main
*/
int main(int, char** argv)
{
	EWC_Open(0, 640, 480, 60);
	EWC_Run(0);
	void* pBuffer = nullptr;
	namedWindow(window_name, WINDOW_AUTOSIZE);
	EWC_GetBuffer(0, &pBuffer);

	Mat dst = Mat(480, 640, CV_8UC4, pBuffer);
	while (true)
	{
		imshow(window_name, dst);

		int c;
		c = waitKey(20);
		if ((char)c == 27)
		{
			break;
		}
	}

	destroyWindow(window_name);
	EWC_Close(0);
}

Visual Studio 2015 でビルドしようとすると qedit.h が足りないと怒られたので、次の記事を参考に解決してみました。
失った時を数えて: #include "qedit.h"におけるエラーの解決方法

実行すると Webcam の映像がウィンドウに表示されます。


この ewclib の中を勉強してみたいと思います。
f:id:simplestar_tech:20160719002833j:plain

OpenCVをインストール

一番新しい OpenCV をもらってきます。

DOWNLOADS | OpenCV

このリンクから OpenCV 3.1 をインストールしました。

次のインストール先のサンプルコードを実行します。
opencv-3.1.0\sources\samples\cpp\tutorial_code\ImgProc

おお、スライダーコントロールで値を変更しながら、結果を確認できるのですね。
C++から直接たたくOpenCVはしばらく触ってませんでしたが
いつのまにか入門者にとって手に取りやすく、コーディングもテストも便利になった感じです。

Threshold を 108 にしたときの lena.png の結果を添付します。

f:id:simplestar_tech:20160717123204j:plain

UnrealEngine4.12をソースコードからVisualStudio2013でビルドする

Epic Gamesのアカウントで次のアドレスに入ったら
https://www.unrealengine.com/dashboard

ソースコードを取得する項目を見つけました。
f:id:simplestar_tech:20160705081425j:plain

GitHub アカウントを作成して、アカウント名をプロフィールに記載することで
Unreal Engineリポジトリアクセス権関連の招待メールが届きました。

招待に応じると、Unreal Engineソースコードにアクセスできるようになりました。
f:id:simplestar_tech:20160705081739j:plain

ここからはビルドするだけです。

GitHubの使い方?
Visual StudioC++ プロジェクトのコンパイルエラー、リンクエラーの解決方法?

はい、詳細を記載していきます。
会社から戻ったらね。

追記;
すまない、その後
Visual Studio 2015 で事足りることがわかったので、追記はやめることにしました。

あと、この記事に興味ある人は
GitHubの使い方を覚えたり
Visual StudioC++ プロジェクトのコンパイルエラー、リンクエラーの解決なんて
自力で調べればすぐですよ。
ということで、次の作業メモ書いていきます。

UnrealEngine4.12をVisualStudio2013で起動する方法

いや、おかしいよ。
何もしなくてもビルドと実行ができるはずなのに(←勘違いでした。)

error LNK2019: unresolved external symbol
という、リンクエラーが発生して先に進めません。

同じような苦しみを味わっている人が数日から数時間前にフォーラムに書き込んでいます。

Log in to your Epic Games account
Log in to your Epic Games account

今のところ回答が付いていません…新参者が行うおバカな質問と解釈されているのか、無視されている感じですね。

そこでだ、検証をしてみることにした。
例えば、Unreal Engine 4.10.2 とかで同じ操作をやったとき何が起きるのかを見てみる。

追記:
ほぅ?
つまり Visual Studio 2015 じゃないとダメ?
f:id:simplestar_tech:20160704221943j:plain

Unreal Engine 4.9.2 を入れて試してみます。

追記:
インストール中に調べてたら、公式回答を見つけました。
UE4 and Visual Studio 2015
Unreal Engine 4.10 からは Visual Studio 2015 でないとプロジェクトのコンパイルは通らないそうです。
どうしても Visual Studio 2013 を使いたい方は Unreal Engine のソースを引いてきて、Visual Studio 2013 を使ってビルドしてください。
とのことです。

はい、確かに Unreal Engine 4.9.2 ならば Visual Studio 2013 でC++プロジェクトを起動できました。
まじか、最新版の Unreal Engine を使いたいならソースからビルドする必要があるのかぁ

次はその記事を書きますか

UnrealEngine4でUTexture2Dをシステムメモリから更新する

まず Unreal Engine 4 ではマテリアルに適用するテクスチャは UTexture2D というものだそうです。(調べただけでまだ触っていない)
基本的にはGPU側で超並列演算を行うもののようですが、特別にシステムメモリのbyte配列からテクスチャの内容を更新するUpdateTextureRegions関数が定義されています。(正しく動くかは知らない、調べただけ)

関連して C++ から WebCam にシンプルにアクセスするには
www.iki.fi/sol - Code - ESCAPI
が知られていて、ライセンスフリーでカメラの解像度を決定できて
各種カメラパラメータを手動 or オートで切り替えられつつ、サンプル時におそらく argb の int 値配列を返すもののようです。(日本語が変でごめん、情報が正しく伝わればよいので、書き換えず、そのままにしてます。)

escapi は IMFSourceReaderというDirectShowの後継を利用していて、ソースも公開されていて、そのバッファを書き込んでいるコードを見ると
https://github.com/jarikomppa/escapi/blob/master/escapi_dll/capture.cpp
どうも、一段不要かもしれない for 文を回して書き込みを行っている箇所があったので
各自、ここを独自に高速化するように書き換えてカメラ画像情報を受け取れるようにすると良さそう。

あとはこれらの知識をつないで、Unreal Engine にて簡易に画像ビューア、コンピュータビジョンを用いたゲームを組めます。

実際に動かしてみないと、調査内容は信用できないので、実際に動いたら追記します。
という、ただの個人メモ

UnrealEngine4.12のC++プラグインでスタティックリンクライブラリを利用する

入門書や公式リファレンスを読んでいてびっくりしたのですが
Unreal Engine におけるプラグイン機能は、次の図で示す通りゲームのプロジェクトにC++ソースコードが配置される感じです。

f:id:simplestar_tech:20160703175311j:plain

プラグインは独立したプロジェクトにならないことに衝撃を受けました。

こんな感じのドキュメントがあるほど
けっこう面倒な処理を手で加えないと
スタティックリンクライブラリを使うプラグインというものを作れないそうなのですよ。

私に限ってかもしれませんが
自分の書くコードにはほとんど価値なんてなくて、世にある優れたライブラリの力を借りつつプラグインって作るものですよね?
そんな作り方が簡単にできない仕組みになっているとは、どういうことなのか!

と、また落とし穴にハマったので抜け出してみたいと思います。

OpenCVをUnreal Engine4で使用する、というドキュメントに参考になるコードがあったので利用します。

追記;
で、実際にスタティックリンクライブラリを介してDLLの実装内容を利用できるところまで確認しましたので要点を記載します。

1.Win32コンソールアプリのテンプレートを利用してDLLを作成します。(シンボルのエクスポートを忘れずに行うこと)
今回は単純に動作確認したいだけだったので、SimpleMathという適当なDLLプロジェクトを用意し、関数を次の図に示す通りに作りました。
f:id:simplestar_tech:20160704081254j:plain
落とし穴がさっそくあります。Unreal Engine 4はx64プロジェクトですので、このDLLもx64でビルドしなければいけません。(DLL機能を使うコードを書いてリンクする時にエラーが出ます。)
作った成果物のうち .lib ファイル(スタティックリンクライブラリ)は次の通りの場所に配置します。
f:id:simplestar_tech:20160704081901j:plain
ThirdParty製ライブラリファイルの配置時にも落とし穴があります。.libとセットで.dllファイルも配置したいところですが、プロジェクトが.dllの配置を見ている箇所は一つしかありません。
次に示すBinaries\Win64フォルダです。(ここにThirdPartyの.dllファイルをすべて配置します。試していませんがたぶんサブフォルダ切れると思います。私が設計担当ならそう作りますので)
f:id:simplestar_tech:20160704082417j:plain

2.プラグイン側でThirdPartyのスタティックリンクライブラリ、ダイナミックリンクライブラリを参照します。
さっそく落とし穴があり、ハマりました。例で示したOpenCVを利用するものを参考にするとコンパイルできなくなります。
Unreal Engine 4.12 からは次のコードにするとコンパイルできるようになるそうです。(ファイルはプラグインのSimplePlugin.Build.csファイルです。C#ファイルなので、C++プロジェクトから参照が正しく機能せず、Unreal Engine特有のクラス定義がどんなフィールドを持っているか、ランタイムで解析するなども行いました。しかし、簡単にプロジェクト名を取得する方法が無いようなので、おかしな文字列操作コードを仕込んでいます。TargetInfoにプロジェクト名とかあると良かったのですけどね、なかったです。)

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using System.Reflection;

namespace UnrealBuildTool.Rules
{
	public class SimplePlugin : ModuleRules
	{
		public SimplePlugin(TargetInfo Target)
		{
			PublicIncludePaths.AddRange(
				new string[] {
					// ... add public include paths required here ...
				}
				);

			PrivateIncludePaths.AddRange(
				new string[] {
					"Developer/SimplePlugin/Private",
					// ... add other private include paths required here ...
				}
				);

			PublicDependencyModuleNames.AddRange(
				new string[]
				{
					"Core",
					// ... add other public dependencies that you statically link with here ...
				}
				);

			PrivateDependencyModuleNames.AddRange(
				new string[]
				{
					// ... add private dependencies that you statically link with here ...
				}
				);

			DynamicallyLoadedModuleNames.AddRange(
				new string[]
				{
					// ... add any modules that your module loads dynamically here ...
				}
				);


            LoadThardParty(Target);
        }

        private string ModulePath
        {
            get {
                FileReference CheckProjectFile;
                string ProjectNameModuleRules = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
                string ProjectName = ProjectNameModuleRules.Substring(0, ProjectNameModuleRules.Length - "ModuleRules".Length);
                UProjectInfo.TryGetProjectForTarget(ProjectName, out CheckProjectFile);
                RulesAssembly r = RulesCompiler.CreateProjectRulesAssembly(CheckProjectFile);
                FileReference f = r.GetModuleFileName(this.GetType().Name);
                return Path.GetDirectoryName(f.CanonicalName);
            }
        }
        private string ThirdPartyPath
        {
            get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
        }

        public bool LoadThardParty(TargetInfo Target)
        {
            bool isLibrarySupported = false;

            // Create Plugin Path 
            string SimpleMathPath = Path.Combine(ThirdPartyPath, "SimpleMath");

            // Get Library Path 
            string LibPath = "";
            bool isdebug = Target.Configuration == UnrealTargetConfiguration.Debug && BuildConfiguration.bDebugBuildsActuallyUseDebugCRT;
            if (Target.Platform == UnrealTargetPlatform.Win64)
            {
                LibPath = Path.Combine(SimpleMathPath, "Libraries", "Win64");
                isLibrarySupported = true;
            }
            else if (Target.Platform == UnrealTargetPlatform.Win32)
            {
                LibPath = Path.Combine(SimpleMathPath, "Libraries", "Win32");
                isLibrarySupported = true;
            }
            else
            {
                string Err = string.Format("{0} dedicated server is made to depend on {1}. We want to avoid this, please correct module dependencies.", Target.Platform.ToString(), this.ToString()); System.Console.WriteLine(Err);
            }

            if (isLibrarySupported)
            {
                //Add Include path 
                PublicIncludePaths.AddRange(new string[] { Path.Combine(SimpleMathPath, "Includes") });

                // Add Library Path 
                PublicLibraryPaths.Add(LibPath);

                // Add Dependencies 
                if (!isdebug)
                {
                    //Add Static Libraries
                    PublicAdditionalLibraries.Add("SimpleMath.lib");

                    //Add Dynamic Libraries
                    PublicDelayLoadDLLs.Add("SimpleMath.dll");
                }
                else
                {
                    //Add Static Libraries (Debug Version)
                    PublicAdditionalLibraries.Add("SimpleMath.lib");

                    //Add Dynamic Libraries (Debug Version)
                    PublicDelayLoadDLLs.Add("SimpleMath.dll");
                }
            }

            // Definitions.Add(string.Format("SOME_PREPROCESSOR={0}", isLibrarySupported ? 1 : 0));

            return isLibrarySupported;
        }
    }
}

3.プラグインコードでThirdPartyのライブラリを利用する。
f:id:simplestar_tech:20160704083700j:plain
この赤い波線が気になるのですよね。
実行時はIncludesへのパスが設定されているのでコンパイルエラーは起こりませんが、Visual Studio でコーディング中はパスの参照が切れているのでヘッダファイルが見つからない旨を教えてくれます。
直接ゲームプロジェクトのインクルードディレクトリをいじれば解決しそうですが、試してはいません。(たぶん解決しますが、プロジェクトを自動生成している関係でいつか誰かがクリアしちゃうものなのだと覚悟する必要があるでしょう)
プラグイン側のテスト実装は次の通りです。(ここにあるコメントの通り、相対パスでIncludesにあるヘッダファイルを指定するとプロジェクト設定に非依存で参照問題を解決できます。)

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

#include "SimplePluginPrivatePCH.h"
#include "SimpleMath.h"
// #include "../../../ThirdParty/SimpleMath/Includes/SimpleMath.h"

class FSimplePlugin : public ISimplePlugin
{
	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
};

IMPLEMENT_MODULE( FSimplePlugin, SimplePlugin )



void FSimplePlugin::StartupModule()
{
	// This code will execute after your module is loaded into memory (but after global variables are initialized, of course.)

	CSimpleMath a;
	int sum = a.Sum3Int(1, 2, 3);
	return;
}


void FSimplePlugin::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.
}

デバッグ実行時に落とし穴がありました。
エディタを起動しながらステップ実行してプラグインのコードを動作確認をしたいなら、ビルド時の構成は次のものを利用します。(初回ビルドに5分ほど時間を要しますよ。)
f:id:simplestar_tech:20160704084404j:plain
ディフォルトではDevelopment Editorになっているようで、そのときの挙動をみるとリリースビルドに近かったです。
Sippingはさらに最適化が施される形なのかなと予想(選んで実行はしていない)

はい、ここまでの1.2.3.で示したステップを踏むことで私の環境ではThridParty製のライブラリをプラグインで利用できることが確認できました。
自分は自分で独自にOpenCVなどのThridParty製のライブラリをUnreal Engineプラグインで利用する予定です。