LoginSignup
4
3

More than 3 years have passed since last update.

(比較的)ストレスの掛からないHalide導入方法

Last updated at Posted at 2020-01-15

この投稿は、研究室の後輩へ向けたものですが、他の人の助けにもなればいいなということで、導入メモとして投稿します。

※色々初心者なので、「ここ違うよ!」・「ここは、この表現が良いよ!」等大歓迎です。

この作業は時間がかかります(大体2, 3時間)。
時間と心にゆとりのある時に実施下さいませ。

Halideって?

さわり初めで、詳しく分からないので
詳しくは、
- Halide公式のサイト
- 株式会社フィックスターズさんの公開SlideShare
を見てください。

めちゃくちゃ高速で画像処理ができるドメイン固有言語です(間違ってたらすいません)。
C++にライブラリとして導入することで使えます。

環境

  • Windows 10
  • Visual Studio 2019

下準備

最初に、ライブラリ導入ソフトであるvcpkgをインストールしなければなりません。
そのvcpkgをインストールするためにgitをインストールする必要があります。
※別に、vcpkgのgithubからクローンしてきてもいいです。
vcpkgでインストールされたライブラリは(ある設定をすれば)Visual Studio(以降VS)で

#include <〇〇.h>

と記述するだけで使える便利な代物です。

gitのインストール

gitの公式サイトへ行き、ダウンロード&インストールします。
導入方法は、こちらのサイトが詳しいです。

vcpkgのインストール

gitのインストールが完了したら、次はvcpkgのインストールです。
コマンドプロンプトかpowershellかwindows terminalを立ち上げます。
その際、どこか分かりやすいところまでカレントディレクトリを移動します。

cd C:\
とか
cd D:\
とか

そのあとに、

git clone https://github.com/Microsoft/vcpkg.git


イメージ図
01.png

をしたら、CドライブやDドライブ直下にvcpkgフォルダが出来ているのが分かると思います。
では、vcpkgをカレントディレクトリとしましょう。

cd vcpkg

次に、vcpkg本体のビルドをする必要があります。

.\bootstrap-vcpkg.bat


イメージ図
05.png
↑が↓になればOK
06.png


なんかエラーが出た時
こんな感じのエラーが出たら、VSにC++が入っていないと怒られちゃっています。
02_C++追加要請.png
なので、ツールと機能を取得から
03_ツールと機能.png
[C++によるデスクトップ開発]にチェックを入れて導入してあげてください。
04.png

そうすれば、きっとうまくいきます。

1. おまじない

.\vcpkg integrate install

と打ってあげましょう。
こうすることで、めんどくさい事せずにVSでライブラリを使えます。


イメージ図
07.png

取り敢えず、ここまで出来たら

.\vcpkg help

と打ってみましょう。使い方のリストが出てきます。
08.png

2. Halide導入

一番時間がかかるところです。

.\vcpkg search halide

と打ってみると、halideの名の付くパッケージがリストアップされます。


イメージ図
09.png

ここではリストの一番上にあるhalideを導入します。

.\vcpkg install halide:x86-windows halide:x64-windows
または
.\vcpkg install halide:x86-windows
や
.\vcpkg install halide:x64-windows
でもいいですここはお好みで。というより、お使いのPCと相談してください。


イメージ図
10.png


なんかエラー出たやん11.png

VSに英語の言語パックを追加しないといけません。
なので、ツールと機能を取得から[言語パック]へ移動し、英語にチェックを入れましょう。
12.png


他にもエラー出たんやけど13.png

これは、ATLとMFCが無いのでVSに入れてねってことです(ATLとMFCが何なのかは私もわかりません)。
なので、ツールと機能を取得から
[最新のv142ビルドツール用C++ ATL], [最新のv142ビルドツール用C++ MFC]にチェックを入れてあげましょう。
14.png

ここまでしたら、あとは放置です。
2, 3時間かかるので、お風呂入っててもいいし、寝ててもいいです。
15.png
完了ですね。
16.png

.\vcpkg list

でさっきインストールしたものを見てみましょう。
17.png
こんな感じになってると思います。

3. VSでHalideをincludeしてみよう

VSでC++を空のプロジェクトとして作成したら、main.cppを作って

#include <Halide.h>

int main(){
    return 0;
}

でうまくいくと思います。
19.png

OpenCVと合わせて使うとき

@fukushima1981さんが投稿された記事の中に載せられてるコードがとても便利です(vcpkgでOpenCVを導入して使ってください)。
しかしながら、vcpkgによりライブラリ群を導入したことで、いくつか消さなければならない記述が出てきます。

#pragma comment(lib, "opencv_core331.lib")
#pragma comment(lib, "opencv_imgcodecs331.lib")
#pragma comment(lib, "opencv_highgui331.lib")
#pragma comment(lib, "Halide.lib")

これらを消さねばなりません。
理由については、コチラのページに詳細が掛かれています。
つまり競合しちゃうわけですね。
それを踏まえて、vcpkgで導入したverのコードを下に示します。
とても便利なので、ヘッダファイルにして、#includeしても良さそうです。

#pragma once

#include <opencv2/opencv.hpp>
#include "Halide.h"

using namespace Halide;

//OpenCV for Halide functions

//中でcv::imreadをコール.
Buffer<uint8_t> imread(cv::String name);

//中でcv::imwriteをコール.
void imwrite(cv::String name, const Buffer<uint8_t>& src);

//HalideのbufferからOpenCVのMatへ変換
void convertHalide2Mat(const Buffer<uint8_t>& src, cv::Mat& dest);

//OpenCVのMatからHalideのbufferへ変換
void convertMat2Halide(cv::Mat& src, Buffer<uint8_t>& dest);

//ucharのバッファ表示用
void imshow(cv::String name, const Buffer<uint8_t>& src);

//shortのバッファ表示用.可視化のデータはucharなのでオフセットとスケールで0~255へのキャストを調整
void imshow16(cv::String name, const Buffer<int16_t>& src, double offset = 0.0, double scale = 1.0);

//アルファブレンドをして出力を比較デバッグ用関数
void guiAlphaBlend(Buffer<uint8_t>& src1, Buffer<uint8_t>& src2, cv::String name = "alpha blend");

Func copy(Buffer<uint8_t>& src)
{
    Var x("x"), y("y"), c("c");

    Func output("output");
    output(x, y, c) = src(x, y, c);

    return output;
}

//utility function with OpenCV
void convertMat2Halide(cv::Mat& src, Buffer<uint8_t>& dest)
{
    const int ch = src.channels();
    if (ch == 1)
    {
        for (int j = 0; j < src.rows; j++)
        {
            for (int i = 0; i < src.cols; i++)
            {
                dest(i, j) = src.at<uchar>(j, i);
            }
        }
    }
    else if (ch == 3)
    {
        for (int j = 0; j < src.rows; j++)
        {
            for (int i = 0; i < src.cols; i++)
            {
                dest(i, j, 0) = src.at<uchar>(j, 3 * i);
                dest(i, j, 1) = src.at<uchar>(j, 3 * i + 1);
                dest(i, j, 2) = src.at<uchar>(j, 3 * i + 2);
            }
        }
    }
}

Buffer<uint8_t> imread(cv::String name)
{
    cv::Mat a = cv::imread(name);
    if (a.empty()) std::cout << name << " is empty" << std::endl;

    Buffer<uint8_t> ret(a.cols, a.rows, a.channels());
    convertMat2Halide(a, ret);

    return ret;
}

void convertHalide2Mat(const Buffer<uint8_t>& src, cv::Mat& dest)
{
    if (dest.empty()) dest.create(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));
    const int ch = dest.channels();
    if (ch == 1)
    {
        for (int j = 0; j < dest.rows; j++)
        {
            for (int i = 0; i < dest.cols; i++)
            {
                dest.at<uchar>(j, i) = src(i, j);
            }
        }
    }
    else if (ch == 3)
    {
        for (int j = 0; j < dest.rows; j++)
        {
            for (int i = 0; i < dest.cols; i++)
            {
                dest.at<uchar>(j, 3 * i + 0) = src(i, j, 0);
                dest.at<uchar>(j, 3 * i + 1) = src(i, j, 1);
                dest.at<uchar>(j, 3 * i + 2) = src(i, j, 2);
            }
        }
    }
}

void imwrite(cv::String name, const Buffer<uint8_t>& src)
{
    cv::Mat a(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));
    convertHalide2Mat(src, a);
    cv::imwrite(name, a);
}

void imshow(cv::String name, const Buffer<uint8_t>& src)
{
    cv::Mat a(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));
    convertHalide2Mat(src, a);
    cv::imshow(name, a);
}

void imshow16(cv::String name, const Buffer<int16_t>& src, double offset, double scale)
{
    cv::Mat a(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));

    const int ch = a.channels();
    if (ch == 1)
    {
        for (int j = 0; j < a.rows; j++)
        {
            for (int i = 0; i < a.cols; i++)
            {
                a.at<uchar>(j, i) = cv::saturate_cast<uchar>(scale * src(i, j) + offset);
            }
        }
    }
    else if (ch == 3)
    {
        for (int j = 0; j < a.rows; j++)
        {
            for (int i = 0; i < a.cols; i++)
            {
                a.at<uchar>(j, 3 * i + 0) = cv::saturate_cast<uchar>(scale * src(i, j, 0) + +offset);
                a.at<uchar>(j, 3 * i + 1) = cv::saturate_cast<uchar>(scale * src(i, j, 1) + +offset);
                a.at<uchar>(j, 3 * i + 2) = cv::saturate_cast<uchar>(scale * src(i, j, 2) + +offset);
            }
        }
    }

    cv::imshow(name, a);
}

void guiAlphaBlend(Buffer<uint8_t>& src1, Buffer<uint8_t>& src2, cv::String name)
{
    cv::Mat s1(cv::Size(src1.width(), src1.height()), CV_MAKETYPE(CV_8U, src1.channels()));
    cv::Mat s2(cv::Size(src1.width(), src1.height()), CV_MAKETYPE(CV_8U, src1.channels()));
    convertHalide2Mat(src1, s1);
    convertHalide2Mat(src2, s2);

    cv::namedWindow(name);
    int a = 0; cv::createTrackbar("alpha", name, &a, 100);
    int key = 0;
    while (key != 'q')
    {
        cv::Mat show;
        cv::addWeighted(s1, 1.0 - a / 100.0, s2, a / 100.0, 0.0, show);
        cv::imshow(name, show);
        key = cv::waitKey(1);
    }
    cv::destroyWindow(name);
}

参考サイト

https://github.com/microsoft/vcpkg
Visual Studio 2017 で MFC アプリ開発機能を追加するには
C/C++ パッケージマネージャvcpkgの使い方
OpenCVによるHalideプログラミングの開発支援
CMake&vcpkgでつまずいた点

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3