これは,Boost Advent Calendar 2011 参加記事です.現在,開催されている Advent Calendar の一覧は Advent Calendar 2011 (jp) 開催予定リスト - Life like a clown を参照下さい.
当初,Boost.GIL の(初歩的な)逆引きリファレンス的な記事にしようかと思ったのですが,Boost.勉強会の Boost.GIL 画像処理入門 (PDF) に大体の事が書かれてあってやる事がなかったので,後半に書く予定だった「画像を明るくする」,「画像を鮮やかにする」等の処理を行うための HSV 変換についてをメインに書くことにします.
これを Boost.GIL で実現する方法ですが,boost::gil::transform_pixel と言う関数を使うと良いようです.
boost::gil::transform_pixel(const View& src, const View& dst, F functor)対象のピクセルを受け取って別のピクセルを返す関数を、全ピクセルに対して適用します。この関数は、画素値と出力値が1対1対応である場合に用います(e.g. 明度、コントラスト, レベル補正, トーンカーブ, etc...)。
boost::gil::transform_pixel_positions(const View& src, const View& dst, F functor)周囲のピクセルの値を元に結果となるピクセルを返す関数を、全ピクセルに対して適用します。transform_pixelとは、受け取る引数がロケータであるという点が異なっており、周囲の画素値を元に実際の出力値を求める場合に有効です(e.g. Blur, Sovel, Laplacian, etc...)。
映像奮闘記: boost.GILで組む、ジェネリックな画像処理のアルゴリズム
尚,第 3 引数に指定する関数オブジェクトは以下のようになります.
class transform_functor { public: template<typename PixelT> PixelT operator()(PixelT src); };
HSV 色空間内で処理を行うための hsvfilter
今回は,この形に沿った関数オブジェクトで「RGB 値を HSV 値に変換してユーザに何らかの処理を行ってもらった後,再び RGB 値に戻す」と言う機能を持つ hsvfilter を実装してみます.尚,現時点では各ピクセルが RGB 値のみを持つ画像でないとエラーになります.それ以外のピクセル型については,そのうち調べて実装していこうかと思います.
#ifndef HSVFILTER_H #define HSVFILTER_H #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <boost/function.hpp> #include <boost/gil/gil_all.hpp> #include <cassert> /* ------------------------------------------------------------------------- */ /* * hsvfilter * * 各ピクセルを HSV 値に変換した上で何らかの処理を行うためのフィルタ. */ /* ------------------------------------------------------------------------- */ class hsvfilter { public: typedef boost::function<void (int&, int&, int&)> predicate_type; private: typedef boost::tuple<int, int, int> rgb_type; typedef boost::tuple<int, int, int> hsv_type; public: /* --------------------------------------------------------------------- */ // constructor /* --------------------------------------------------------------------- */ explicit hsvfilter(predicate_type pred) : pred_(pred) {} /* --------------------------------------------------------------------- */ // operator() /* --------------------------------------------------------------------- */ template <class PixelT> PixelT operator()(PixelT pixel) { rgb_type src = this->to_rgb(pixel); hsv_type hsv = this->rgb2hsv(src); pred_(hsv.get<0>(), hsv.get<1>(), hsv.get<2>()); rgb_type dest = this->hsv2rgb(hsv); return this->from_rgb<PixelT>(dest); } private: /* --------------------------------------------------------------------- */ /* * to_rgb * * Boost.GIL の各種ピクセル型から RGB へ変換する. * TODO: 現在は RGB の値のみが格納されてあるものしかサポートされて * いない.その他のピクセル型を調査して対応する. */ /* --------------------------------------------------------------------- */ template <class PixelT> rgb_type to_rgb(const PixelT& pixel) { assert(boost::gil::num_channels<PixelT>::value == 3); return boost::make_tuple(pixel[0], pixel[1], pixel[2]); } /* --------------------------------------------------------------------- */ /* * from_rgb * * RGB から Boost.GIL の各種ピクセル型へ変換する. * TODO: 現在は RGB の値のみが格納されてあるものしかサポートされて * いない.その他のピクセル型を調査して対応する. */ /* --------------------------------------------------------------------- */ template <class PixelT> PixelT from_rgb(const rgb_type& rgb) { assert(boost::gil::num_channels<PixelT>::value == 3); PixelT dest; dest[0] = rgb.get<0>(); dest[1] = rgb.get<1>(); dest[2] = rgb.get<2>(); return dest; } /* --------------------------------------------------------------------- */ /* * rgb2hsv * * RGB 値を HSV 値に変換する. */ /* --------------------------------------------------------------------- */ hsv_type rgb2hsv(const rgb_type& rgb) { const int R = std::min(rgb.get<0>(), 255); const int G = std::min(rgb.get<1>(), 255); const int B = std::min(rgb.get<2>(), 255); const int maximum = std::max(std::max(R, G), B); const int minimum = std::min(std::min(R, G), B); const double V = maximum; const double S = (maximum != 0) ? 255.0 * (maximum - minimum) / static_cast<double>(maximum) : 0.0; double H = 0.0; if (S > 0.0 && maximum != minimum) { if (maximum == R) H = 60.0 * (G - B) / static_cast<double>(maximum - minimum); else if (maximum == G) H = 60.0 * (B - R) / static_cast<double>(maximum - minimum) + 120.0; else H = 60.0 * (R - G) / static_cast<double>(maximum - minimum) + 240.0; if (H < 0.0) H += 360.0; } return boost::make_tuple(static_cast<int>(H), static_cast<int>(S), static_cast<int>(V)); } /* --------------------------------------------------------------------- */ /* * hsv2rgb * * HSV 値を RGB 値に変換する. */ /* --------------------------------------------------------------------- */ rgb_type hsv2rgb(const hsv_type& hsv) { const int H = hsv.get<0>(); const int S = hsv.get<1>(); const int V = hsv.get<2>(); if (S == 0) return boost::make_tuple(V, V, V); const int Hi = H / 60; const double F = H / 60.0 - Hi; const double M = (1.0 - S / 255.0) * V; const double N = (1.0 - S / 255.0 * F) * V; const double K = (1.0 - S / 255.0 * (1.0 - F)) * V; switch (Hi) { case 0: return boost::make_tuple(V, K, M); case 1: return boost::make_tuple(N, V, M); case 2: return boost::make_tuple(M, V, K); case 3: return boost::make_tuple(M, N, V); case 4: return boost::make_tuple(K, M, V); case 5: return boost::make_tuple(V, M, N); default: break; } return boost::make_tuple(-1, -1, -1); } private: predicate_type pred_; }; #endif // HSVFILTER_H
サンプルプログラム
この hsvfilter を使用して,画像を明るくするサンプルプログラムは以下になります.ここでは画像の明るさを調整していますが,これが例えば,画像の鮮やかさを調整する場合は,value の代わりに saturation の値を弄る事になります.
/* ------------------------------------------------------------------------- */ /* * brightener * * 各ピクセルの明るさを一律に明るくしていくクラス. */ /* ------------------------------------------------------------------------- */ class brightener { public: explicit brightener(int brightness) : brightness_(brightness) {} void operator()(int& hue, int& saturation, int& value) { value += brightness_; if (value > 255) value = 255; } private: int brightness_; }; /* ------------------------------------------------------------------------- */ // main /* ------------------------------------------------------------------------- */ int main(int argc, char* argv[]) { if (argc < 3) return -1; std::string src_path = argv[1]; std::string dest_path = argv[2]; boost::gil::rgb8_image_t src_image; boost::gil::jpeg_read_image(src_path, src_image); boost::gil::rgb8_image_t dest_image(src_image.dimensions()); // メイン処理 int brightness = 50; boost::gil::transform_pixels( boost::gil::const_view(src_image), boost::gil::view(dest_image), hsvfilter(brightener(brightness)) ); boost::gil::jpeg_write_view(dest_path, boost::gil::view(dest_image)); return 0; }