none
openCVを利用した輝度調整の方法 RRS feed

  • 質問

  • いつも大変お世話になっています。

    Win7 Visual Studio 2008 で開発しています。

    画像処理について、現在使用しているライブラリから、openCVに切り替えようとしているのですが、

    輝度調整がうまくいきません。

    前のライブラリでは、引数に、(10.0)~(-10.0)までの値を渡すと、輝度がほとんど白からほとんど黒まで変化していました。

    openCVを使って同じような効果を得る方法がわかりません。

    ちなみに現在使用しているライブラリの関数説明欄には

    X:元のピクセル値,y:新しいピクセル値とすると                      
    y = x + (SQRT(x) - x) * Bright となる 

    と、書かれていました。(Bright =(10.0)~(-10.0))

    意味もわからず、そのように組んでみましたがうまくいきませんでした。

    輝度調整の方法をご存知の方がおられましたら、

    よろしくご教授くださいませ。

    --------------------------------------

        for (y = 0; y < img->height; y++) {
            for (x = 0; x < img->width; x++) {
                for(i=0; i < 3; i++) {
                    brg = img->imageData[img->widthStep * y + x * 3 + i];        // B R G 順
                    brg = brg + uchar( (sqrt((double)brg) - (double)brg) * Bright );
                    brg = min(brg , 255);
                    brg = max(brg , 0);
                    img->imageData[img->widthStep * y + x * 3 + i] = brg;
                }
            }
        }

    --------------------------------------

    2013年10月9日 5:28

すべての返信

  • あまりOpenCVと関係なさそうです。

    RGB値を直接操作しても輝度は意図通りには変わりません。色空間 / RGB / 輝度について勉強することをお勧めします。

    # ところでこういう操作をGPU上で行うのがOpenCVの役目だと思っていました。CPUで行うなら別にOpenCVは要らないような…。

    2013年10月9日 6:39
  • ありがとうございました。

    さらに勉強してみます。

    2013年10月10日 1:59
  • 「現在使用しているライブラリ」というのがよくわからないので比較のしようがないですが、とりあえずそれっぽいのを書いてみました。VC++ 2012とOpenCV 2.4.7で動作確認済み。当方Master of OpenCVではないのでもっと最適化できる可能性はあります。ちなみにOpenCVの画像バッファはカラー24bppの場合BGR,BGR,...の順序で並んでます(Windows Bitmapと同じ)。

    OpenCVはもともとクロスプラットフォームなCPU向け高速画像処理ライブラリとして開発されたのが始まりです。SSE拡張命令による高速化はともかく、CUDAやOpenCLGPUアクセラレートする機能が追加されたのは最近のことです。CUDAにはIPPに似たNPPというユーティリティライブラリが存在し、OpenCV同様に高速画像処理フィルターとしてそのまま活用できますが、OpenCL標準にはありません。なお、今回のようにOpenCVに用意されているcvConvertScale()などの定義済みフィルターが使えないような複雑なカーネルコードをどうしても書きたい場合、生画像バッファを直接操作する必要があると思いますが、だとしても移植性の高いプラットフォーム非依存コードを記述しやすいという意味でOpenCVを使う意義は十分あります。最近ではAndroidやiOS向けのモバイル実装もあります。

    #ifndef __cplusplus
    #error Use C++ instead of C!!
    #endif

    #define _CRT_SECURE_NO_WARNINGS
    // OpenCV 2.4.7 の "opencv2/flann/logger.h" 内で fopen() が使われているため、
    // VC++ 2012 では _CRT_SECURE_NO_WARNINGS 定義がないとコンパイル エラーになる。
    // また、"opencv2/legacy/compat.hpp" に C3H+96H の非 ASCII バイト列が含まれるため、警告 C4819 が出る。
    #include <cstdio>
    #pragma warning(push)
    #pragma warning(disable:4819)
    #include <opencv/cv.h>
    #include <opencv/highgui.h>
    #pragma warning(pop)
    #undef _CRT_SECURE_NO_WARNINGS
    #include <cassert>
    #include <algorithm>
    #include <conio.h>

    #pragma comment(lib, "vc11\\lib\\opencv_core247.lib")
    #pragma comment(lib, "vc11\\lib\\opencv_highgui247.lib")

    // VC++ 2010 以降は <cstdint> を使えばよい。
    typedef unsigned char uint8_t;

    namespace
    {
    template<typename T> const T& Clamp(const T& x, const T& minVal, const T& maxVal)
    {
    if (x < minVal) return minVal;
    if (x > maxVal) return maxVal;
    return x;
    }

    template<typename T> T* SafePointerCast(void* p)
    { return static_cast<T*>(p); }

    template<typename T> const T* SafePointerCast(const void* p)
    { return static_cast<const T*>(p); }

    template<typename T> inline double MyContrastFunc(T src, double brightScale)
    {
    return src + (sqrt(src) - src) * brightScale;
    }

    inline uint8_t ClampMyContrastFunc(uint8_t src, double brightScale)
    {
    return uint8_t(Clamp<double>(MyContrastFunc(src, brightScale), 0, 255));
    }

    const double NtscGrayCoeffR = 0.298912;
    const double NtscGrayCoeffG = 0.586611;
    const double NtscGrayCoeffB = 0.114478;

    inline uint8_t ToNtscGrayscale(uint8_t red, uint8_t green, uint8_t blue)
    {
    return uint8_t(NtscGrayCoeffR * red + NtscGrayCoeffG * green + NtscGrayCoeffB * blue);
    }

    // http://ja.wikipedia.org/wiki/YUV

    void RgbToYCbCr(uint8_t red, uint8_t green, uint8_t blue, double& y, double& cb, double& cr)
    {
    y  = +NtscGrayCoeffR * red + NtscGrayCoeffG * green + NtscGrayCoeffB * blue;
    cb = -0.168736 * red - 0.331264 * green + 0.5 * blue;
    cr = 0.5 * red - 0.418688 * green - 0.081312 * blue;
    }

    void YCbCrToRgb(double y, double cb, double cr, uint8_t& red, uint8_t& green, uint8_t& blue)
    {
    red   = uint8_t(Clamp<double>((y + 1.402 * cr), 0, 255));
    green = uint8_t(Clamp<double>((y - 0.344136 * cb - 0.714136 * cr), 0, 255));
    blue  = uint8_t(Clamp<double>((y + 1.772 * cb), 0, 255));
    }

    inline uint8_t AppendColorVal(uint8_t color, double diff)
    {
    //return uint8_t(Clamp<double>(color * (1 + diff / 255), 0, 255));
    return uint8_t(Clamp<double>(color + diff, 0, 255));
    }

    void DoMyContrastProc(IplImage* dstImg, const IplImage* srcImg, double brightScale)
    {
    assert(
    (dstImg->width == srcImg->width) &&
    (dstImg->height == srcImg->height) &&
    (dstImg->widthStep == srcImg->widthStep)
    );
    const uint8_t* srcDib = SafePointerCast<const uint8_t>(srcImg->imageData);
    uint8_t* dstDib = SafePointerCast<uint8_t>(dstImg->imageData);
    if (srcImg->depth == 8)
    {
    if (srcImg->nChannels == 1)
    {
    printf("Grayscale format. BrightScale = %+5.1f\n", brightScale);
    for (int y = 0; y < srcImg->height; ++y)
    {
    for (int x = 0; x < srcImg->width; ++x)
    {
    const int index = srcImg->widthStep * y + x;
    dstDib[index] = ClampMyContrastFunc(srcDib[index], brightScale);
    }
    }
    return;
    }
    else if (srcImg->nChannels == 3)
    {
    printf("Color BGR format. BrightScale = %+5.1f\n", brightScale);
    for (int y = 0; y < srcImg->height; ++y)
    {
    for (int x = 0; x < srcImg->width; ++x)
    {
    // HLSL, GLSL, OpenCL, CUDA などを使って GPU アクセラレートする場合、
    // この処理をピクセル シェーダーやカーネル関数で実行する。
    // OpenCV にも OpenCL アクセラレートできる ocl モジュールや、
    // CUDA でアクセラレートできる gpu モジュールが用意されているが、
    // 基本的に定義済みフィルターを組み合わせて実行するタイプなので、
    // カーネル関数を直接制御することはできない?
    const int index = srcImg->widthStep * y + x * 3;
    const uint8_t b1 = srcDib[index + 0];
    const uint8_t g1 = srcDib[index + 1];
    const uint8_t r1 = srcDib[index + 2];
    #if 1
    double srcGray = 0, srcCb = 0, srcCr = 0;
    uint8_t r2 = 0, g2 = 0, b2 = 0;
    RgbToYCbCr(r1, g1, b1, srcGray, srcCb, srcCr);
    const double newGray = MyContrastFunc(srcGray, brightScale);
    YCbCrToRgb(newGray, srcCb, srcCr, r2, g2, b2);
    #else
    const uint8_t srcGray = ToNtscGrayscale(r1, g1, b1);
    const double newGray = MyContrastFunc(srcGray, brightScale);
    const double diff = (newGray - srcGray);
    const uint8_t b2 = AppendColorVal(b1, diff);
    const uint8_t g2 = AppendColorVal(g1, diff);
    const uint8_t r2 = AppendColorVal(r1, diff);
    #endif

    #if 1
    dstDib[index + 0] = b2;
    dstDib[index + 1] = g2;
    dstDib[index + 2] = r2;
    #else
    // 原画像がグレースケールの場合との比較確認用。
    const uint8_t gray2 = ToNtscGrayscale(r2, g2, b2);
    dstDib[index + 0] = gray2;
    dstDib[index + 1] = gray2;
    dstDib[index + 2] = gray2;
    #endif
    }
    }
    return;
    }
    }
    puts("Not supported format.");
    }
    }

    int main(int argc, char* argv[])
    {
    // ファイル パスに日本語が含まれる場合、UTF-8 形式に変換する必要があるはず。
    // テスト画像入手元:
    // http://www.ess.ic.kanagawa-it.ac.jp/app_images_j.html
    #if 1
    const char* imgfilePath = "Lenna.bmp";
    #else
    const char* imgfilePath = "Lenna_gray.bmp";
    #endif

    // 画像の読み込み。
    IplImage* srcImg = cvLoadImage(imgfilePath, CV_LOAD_IMAGE_ANYCOLOR | CV_LOAD_IMAGE_ANYDEPTH);
    if (!srcImg)
    {
    printf("Failed to load the image file: <%s>\n", imgfilePath);
    _getch();
    return -1;
    }
    IplImage* dstImg = cvCloneImage(srcImg);
    assert(dstImg != NULL);

    const char* windowName = "OpenCV Test";

    cvNamedWindow(windowName, CV_WINDOW_AUTOSIZE);

    cvShowImage(windowName, dstImg);

    const int EscKey = 27;
    int key = 0;
    double brightScale = 0;
    do
    {
    // キー入力待ち
    key = cvWaitKey(0);
    switch (key)
    {
    case 'b':
    brightScale -= 0.5;
    DoMyContrastProc(dstImg, srcImg, brightScale);
    cvShowImage(windowName, dstImg);
    break;
    case 'd':
    brightScale += 0.5;
    DoMyContrastProc(dstImg, srcImg, brightScale);
    cvShowImage(windowName, dstImg);
    break;
    default:
    break;
    }
    } while (key != EscKey);

    cvDestroyWindow(windowName);

    // 生成した画像データを解放する。
    cvReleaseImage(&srcImg);
    cvReleaseImage(&dstImg);

    return 0;
    }





    • 編集済み sygh 2013年11月15日 13:14
    2013年11月14日 12:55