質問者
openCVを利用した輝度調整の方法

質問
-
いつも大変お世話になっています。
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;
}
}
}
--------------------------------------
すべての返信
-
「現在使用しているライブラリ」というのがよくわからないので比較のしようがないですが、とりあえずそれっぽいのを書いてみました。VC++ 2012とOpenCV 2.4.7で動作確認済み。当方Master of OpenCVではないのでもっと最適化できる可能性はあります。ちなみにOpenCVの画像バッファはカラー24bppの場合BGR,BGR,...の順序で並んでます(Windows Bitmapと同じ)。
※OpenCVはもともとクロスプラットフォームなCPU向け高速画像処理ライブラリとして開発されたのが始まりです。SSE拡張命令による高速化はともかく、CUDAやOpenCLでGPUアクセラレートする機能が追加されたのは最近のことです。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