簡単な低級画像処理をC++でやろう!

opencvなんて潤沢なライブラリに飽きてませんか?中身に興味ありませんか?


という訳で,点の書き換えと読み込み保存しか出来ないような,ローレベルなライブラリで画像処理をやってみましょう.


画像の入出力ライブラリは,以前にC言語のbmp入出力を書いていたので,それをC++14に書き換えました.
https://github.com/buyoh/codeLib/blob/master/cpp/implement/file/bitmap.cpp

外枠はこんな感じです.より多様な形式のファイルを読み込めるように改良するかもしれませんが,編集操作はoperator()のみです.


namespace Img {

    struct RGB {
        uint8_t b;
        uint8_t g;
        uint8_t r;
        RGB(int r, int g, int b) :b(b), g(g), r(r) {}
        RGB(const char* ptr) :b(ptr[0]), g(ptr[1]), r(ptr[2]) {}

        inline uint32_t val() const { return (uint32_t)(r << 16) | (uint32_t)(g << 8) | (uint32_t)(b); }
        inline bool operator==(const RGB& e) const { return b == e.b&&g == e.g&&r == e.r; }
        inline bool operator!=(const RGB& e) const { return b != e.b || g != e.g || r != e.r; }
        inline bool operator <(const RGB& e) const { return val() < e.val(); }
    };

    class Bitmap {
    public:
        int width, height;
        vector<uint8_t> raw;

        Bitmap() {}
        Bitmap(int w, int h) { resize(w, h); }
        void resize(int w, int h) ;

        inline RGB& operator()(int x, int y) { return *(RGB*)(&raw[(y*width + x) * 3]); }
        inline RGB operator()(int x, int y) const { return RGB((const char*)&raw[(y*width + x) * 3]); }

        bool load(const char* filename);
        bool save(const char* filename);
    };
}


前提

ソースコードはusing namespace std;されている上に,次のような競技プログラミングマクロを使っています.
マクロアレルギーの方は注意です.

#define ALL(v) (v).begin(),(v).end()
#define repeat(l) for(auto cnt=0;cnt<(l);++cnt)
#define iterate(b,e) for(auto cnt=(b);cnt!=(e);++cnt)

いくつか書いてみた.解説もあるよ.


左右反転+半分グレースケール化


    const char* filename = "d:\\images\\kosuzu.bmp";
    const char* filename_out1 = "x:\\kosuzu_1.bmp";

    Img::Bitmap bmp;
    assert(bmp.load(filename));

    Img::Bitmap bmp_work = bmp;
    for (int y = 0; y < bmp_work.height; ++y) {
        for (int x = 0; x < bmp_work.width / 2; ++x) {
            swap(bmp_work(x, y), bmp_work(bmp_work.width - x - 1, y));
            bmp_work(x, y).r = bmp_work(x, y).g = bmp_work(x, y).b
                = (bmp_work(x, y).r + bmp_work(x, y).g + bmp_work(x, y).b) / 3;
        }
    }
    assert(bmp_work.save(filename_out1));
  • TODO:画像
  • std::swapで交換できたり,色の操作も直感的に出来る.直感的だよね?
  • 平均を取ってるだけなので,YCrCbの明度変換では無い.

レンズの歪曲表現


    const char* filename = "d:\\images\\kosuzu.bmp";
    const char* filename_out2 = "x:\\kosuzu_2.bmp";

    Img::Bitmap bmp;
    assert(bmp.load(filename));

    Img::Bitmap bmp_work(bmp.width, bmp.height);
    int cx = bmp.width / 2;
    int cy = bmp.height / 2;
    fill(ALL(bmp_work.raw), 255);

    for (int y = 0; y < bmp.height; ++y) {
        for (int x = 0; x < bmp.width; ++x) {
            // 本来は逆変換を書くべき.
            int r2 = (x - cx)*(x - cx) + (y - cy)*(y - cy);
            int tx = cx + (double)(x - cx)*(1 - 0.00002*r2);
            int ty = cy + (double)(y - cx)*(1 - 0.00002*r2);
            if (0 <= tx && tx < bmp_work.width && 0 <= ty && ty < bmp_work.height)
                bmp_work(tx, ty) = bmp(x, y);
        }
    }
    assert(bmp_work.save(filename_out2));
  • TODO:画像
  • カメラ キャリブレーションとは - MathWorksの半径方向の歪みを実装した.
  • 逆変換を実装したわけではないので,-0.00002を正の値にする(拡大変換する)と,画素がまばらになって出力する.
  • 線形変換ではないので,逆変換は簡単には求まらない.

八色の読書家

ながいよ.


    const char* filename = "d:\\images\\kosuzu.bmp";
    const char* filename_out3 = "x:\\kosuzu_3.bmp";

    Img::Bitmap bmp;
    assert(bmp.load(filename));

    vector colors;
    repeat(2000) {
        colors.push_back(bmp(rand_int(0, bmp.width - 1), rand_int(0, bmp.height - 1)));
    }

    vector> rgblists;
    vector rgbidx(colors.size());
    rgblists.push_back({ 0,0,0,0 });
    rgblists.push_back({ 255,0,0,0 });
    rgblists.push_back({ 0,255,0,0 });
    rgblists.push_back({ 0,0,255,0 });
    rgblists.push_back({ 255,255,0,0 });
    rgblists.push_back({ 255,0,255,0 });
    rgblists.push_back({ 0,255,255,0 });
    rgblists.push_back({ 255,255,255,0 });

    repeat(10) {
        // classify
        repeat(colors.size()) {
            const Img::RGB& rgb = colors[cnt];
            int dist = 1 << 20;
            int idx = 0;
            repeat(rgblists.size()) {
                int d
                    = abs(rgb.r - rgblists[cnt][0])
                    + abs(rgb.g - rgblists[cnt][1])
                    + abs(rgb.b - rgblists[cnt][2]);
                if (d < dist) {
                    idx = cnt; dist = d;
                }
            }
            rgbidx[cnt] = idx;
        }
        // regenerate
        for (vector& v : rgblists) fill(ALL(v), 0);
        repeat(colors.size()) {
            auto& v = rgblists[rgbidx[cnt]];
            v[0] += colors[cnt].r;
            v[1] += colors[cnt].g;
            v[2] += colors[cnt].b;
            v[3] += 1;
        }
        for (vector& v : rgblists) {
            if (v[3] > 1) {
                v[0] /= v[3];
                v[1] /= v[3];
                v[2] /= v[3];
            }
        }
    }

    Img::Bitmap bmp_work(bmp.width, bmp.height);

    for (int y = 0; y < bmp.height; ++y) {
        for (int x = 0; x < bmp.width; ++x) {
            Img::RGB rgb = bmp(x, y);

            int dist = 1 << 20;
            int idx = 0;
            repeat(rgblists.size()) {
                int d
                    = abs(rgb.r - rgblists[cnt][0])
                    + abs(rgb.g - rgblists[cnt][1])
                    + abs(rgb.b - rgblists[cnt][2]);
                if (d < dist) {
                    idx = cnt; dist = d;
                }
            }
            Img::RGB& newrgb = bmp_work(x, y);
            newrgb.g = rgblists[idx][2];
            newrgb.b = rgblists[idx][1];
            newrgb.r = rgblists[idx][0];
        }
    }
    assert(bmp_work.save(filename_out3));
  • TODO:画像
  • どう見てもk-mensです本当にありがとうございました.
  • ランダムな2000点の座標から色を取り出して,その2000個の色を使ってk-meansでクラスタリング,RGB空間を8分割する.
  • 変色した感じなのは,平均の色を使っているから.
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

tag : c++ 画像解析

コメントの投稿

非公開コメント

プロフィール

舞葉(ぶよう)

Author:舞葉(ぶよう)
github.io
はてなブログ(競プロ)

古い記事のソースコードは色分けしていないので、高機能テキストエディタに貼り付けたほうが見やすいかも。

検索フォーム
このブログについて
自分がつまづいた話題、なんとなく書きたいと思ったこと、ググったけど殆ど資料なかったぞオイ な話等をアップする予定。通りすがりでも、参考になっていただければと。プログラムの例外入力、メモリリークは責任負いません。投稿された記事は修正・削除する場合があります。
カテゴリ
タグ

HSP3アルゴリズムとデータ構造c++RubyJavaUnity画像解析C機械学習C#LinuxcodeIQKinectMinecraftTonyuSystemraspberrypiPythonHTML5音声制御Simulinkruby俺ルール通信制御Javascriptシミュレーション

counter-shinobi
固定記事
最新記事
最新コメント
月別アーカイブ
ブロとも申請フォーム

この人とブロともになる

アクセスランキング
[ジャンルランキング]
コンピュータ
1373位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
237位
アクセスランキングを見る>>