LoginSignup
4
2

More than 5 years have passed since last update.

Halideによるポイントオペレータの実装

Last updated at Posted at 2017-11-19

はじめに

Halideのリンクは以下の記事にまとめてあります.
Halideによる画像処理まとめ

本記事は,その中でもポイントオペレータの入門用です.

備考
サンプルコード中,OpenCVによるHalideプログラミングの開発支援で書いたOpenCVのコードを使って可視化していますが,Halideで書いたプログラミングに必須なものではありません.

ポイントオペレータの書き方と条件分岐

実装した関数

以下の関数を実装した.

  • 加算とその駄目な例
    • ucharの加算は255を超えると一周するのでshortでキャストすること.
    • saturating_castでクリップするもしくはclampで値をクリップすること.
    • 飽和演算(_mm_adds_epu8など)ができればucharのままでもいいが,やり方がわからない.
  • 乗算とその駄目な例
    • ucharの乗算は命令にない&255を超えると一周するのでshortでキャストすること.
    • 負の乗算は正数を対象にしたため対象外のため,clampではなくminでよい.
    • もしくはsaturating_castでも可.
  • 浮動小数点の乗算
    • saturating_castでも可.
  • 閾値処理とその駄目な例
    • 定数値を必ずucharでキャストすること.関数型は型が大事.
    • if文ではなくてselect文で条件分岐させる.excelのif関数っぽい使い方.
  • 色変換(RGB2GrayとRGB2YUV)
    • (YUV変換は,これが最適なコードじゃない気がする)
#include <opencv2/opencv.hpp>
#pragma comment(lib, "opencv_core320.lib")
#pragma comment(lib, "opencv_imgproc320.lib")
#pragma comment(lib, "opencv_imgcodecs320.lib")
#pragma comment(lib, "opencv_highgui320.lib")

#include "include/Halide.h"
#pragma comment(lib, "Halide.lib")

using namespace Halide;

//中でcv::imreadをコール.
Buffer<uint8_t> load_image(cv::String name, int flags = 1);
//中でcv::imwriteをコール.
void save_image(const Buffer<uint8_t>& src, cv::String name);

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

Func add_bad(Buffer<uint8_t>& src, int add)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = min(255, src(x, y, c) + add);
    return output;
}

Func add(Buffer<uint8_t>& src, int add)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = cast<uint8_t>(clamp(cast<int16_t>(src(x, y, c)) + (short)add, 0, 255));
    return output;
}

Func sub(Buffer<uint8_t>& src, int add)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = saturating_cast<uint8_t>(cast<int16_t>(src(x, y, c)) - (short)add);
    return output;
}

Func mul_bad(Buffer<uint8_t>& src, int scale)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = min(255, scale*src(x, y, c));
    return output;
}

Func mul(Buffer<uint8_t>& src, int scale)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = cast<uint8_t>(min(255, scale*cast<uint16_t>(src(x, y, c))));
    return output;
}

Func mul(Buffer<uint8_t>& src, float scale)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = cast<uint8_t>(clamp(scale*cast<float>(src(x, y, c)), 0, 255));
    return output;
}

Func threshold_bad(Buffer<uint8_t>& src, int thresh)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = (select(src(x, y, c) >= thresh, 255, 0));
    return output;
}

Func threshold(Buffer<uint8_t>& src, int thresh)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    output(x, y, c) = (select(src(x, y, c) >= thresh, cast<uint8_t>(255), cast<uint8_t>(0)));
    return output;
}

Func rgbgray(Buffer<uint8_t>& src)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    Func src32f("src32f");
    src32f(x, y, c) = cast<float>(src(x, y, c));
    output(x, y, c) = cast<uint8_t>(0.299f*src32f(x, y, 0) + 0.587f*src32f(x, y, 1) + 0.114f*src32f(x, y, 2));
    return output;
}

Func rgbyuv(Buffer<uint8_t>& src)
{
    Var x("x"), y("y"), c("c");
    Func output("output");
    Func src32f("src32f");
    src32f(x, y, c) = cast<float>(src(x, y, c));
    output(x, y, c) = cast<uint8_t>(select(c == 0, 0.299f*src32f(x, y, 0) + 0.587f*src32f(x, y, 1) + 0.114f*src32f(x, y, 2),
        c == 1, 0.615f*src32f(x, y, 0) - 0.51499f*src32f(x, y, 1) - 0.10001f*src32f(x, y, 2) + 128.f,
        /*else*/       -0.14713f*src32f(x, y, 0) - 0.28886f*src32f(x, y, 1) + 0.436f*src32f(x, y, 2) + 128.f));
    return output;
}

int main(int argc, char **argv)
{
    Buffer<uint8_t> input = load_image("rgb.png");

    Func output0 = add_bad(input, 50);
    Buffer<uint8_t> result0 = output0.realize(input.width(), input.height(), 3);
    imshow("add_bad", result0);

    Func output1 = add(input, 50);
    Buffer<uint8_t> result1;
    int64 start = cv::getTickCount();
    result1 = output1.realize(input.width(), input.height(), 3);
    imshow("add", result1);

    output1 = sub(input, 50);
    result1;
    result1 = output1.realize(input.width(), input.height(), 3);
    imshow("sub", result1);

    Func output2 = mul_bad(input, 2);
    Buffer<uint8_t> result2 = output2.realize(input.width(), input.height(), 3);
    imshow("mul_bad", result2);

    Func output3 = mul(input, 2);
    Buffer<uint8_t> result3 = output3.realize(input.width(), input.height(), 3);
    imshow("mul", result3);

    Func output4 = mul(input, 1.5f);
    Buffer<uint8_t> result4 = output4.realize(input.width(), input.height(), 3);
    imshow("mul_32f", result4);

    Func output5 = threshold_bad(input, 100);
    Buffer<uint8_t> result5 = output5.realize(input.width(), input.height(), 3);
    imshow("threshold_bad", result5);

    Func output6 = threshold(input, 100);
    Buffer<uint8_t> result6 = output6.realize(input.width(), input.height(), 3);
    imshow("threshold", result6);

    Func output7 = rgbgray(input);
    Buffer<uint8_t> result7 = output7.realize(input.width(), input.height(), 3);
    imshow("rgbgray", result7);

    Func output8 = rgbyuv(input);
    Buffer<uint8_t> result8 = output8.realize(input.width(), input.height(), 3);
    imshow("rgbyuv", result8);

    cv::Mat s = cv::imread("rgb.png");
    cv::cvtColor(s, s, cv::COLOR_BGR2YUV);
    imshow("opencvyuv", s);

    cv::waitKey();
    return 0;
}

//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> load_image(cv::String name, int flags)
{
    cv::Mat a = cv::imread(name, flags);
    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 save_image(const Buffer<uint8_t>& src, cv::String name)
{
    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, int offset)
{
    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>(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>(src(i, j, 0) + +offset);
                a.at<uchar>(j, 3 * i + 1) = cv::saturate_cast<uchar>(src(i, j, 1) + +offset);
                a.at<uchar>(j, 3 * i + 2) = cv::saturate_cast<uchar>(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);
}
4
2
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
2