解説
通常用いるRGB配列の画像を一度HSVに変換し、輝度を示すVを少し増やして再びRGB配列に 戻した。変換公式は...微妙に複雑なので知りたい人は「HSV色空間」や「RGB HSV」などの単語 で検索をかけて調べてほしい。
実行例

ソースコード
WTLのプロジェクトウイザードを使ってSDI Apllicationプロジェクトを作成し、View用の ヘッダーファイルを以下のように変える。ここではプロジェクト名を「ImageEffect111」と しています。// ImageEffect111View.h : interface of the CImageEffect111View class // ///////////////////////////////////////////////////////////////////////////// #pragma once #include#pragma comment(lib,"Gdiplus.lib") class CImageEffect111View : public CWindowImpl { public: DECLARE_WND_CLASS(NULL) BOOL PreTranslateMessage(MSG* pMsg) { pMsg; return FALSE; } BEGIN_MSG_MAP(CImageEffect111View) MESSAGE_HANDLER(WM_PAINT, OnPaint) END_MSG_MAP() // Handler prototypes (uncomment arguments if needed): // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) double Min(double a,double b,double c) { if(a <= b && a <= c) return a; if(b <= a && b <= c) return b; return c; } double Max(double a,double b,double c) { if(a >= b && a >= c) return a; if(b >= a && b >= c) return b; return c; } void RGBtoHSV(RGBTRIPLE rgb,double& dH,double& dS,double& dV) { double dR; double dG; double dB; double dMax; double dMin; double dDiff; //RGB->HSV変換時のR,G,Bは 0.0~1.0 dR = rgb.rgbtRed / 255.0; dG = rgb.rgbtGreen / 255.0; dB = rgb.rgbtBlue / 255.0; dMax = Max(dR,dG,dB); dMin = Min(dR,dG,dB); if(dMax == 0) //巷のコードではdMax==dMinで処理していることがあるがそれはNG { dS = 0.0; dV = 0.0; return; } dDiff = dMax - dMin; dV = dMax; dS = dDiff / dMax; //dS = dMax - dMin; //円錐色空間時のS if(dR == dMax) dH = 60 * (dG - dB) / dDiff; else if(dG == dMax) dH = 60 * (dB - dR) / dDiff + 120; else dH = 60 * (dR - dG) / dDiff + 240; //上の計算ではdHが負になることがある if(dH < 0) dH = dH + 360; return; } void HSVtoRGB(double dH,double dS,double dV,RGBTRIPLE& rgb) { if(dS == 0.0) { rgb.rgbtRed = (BYTE)(dV * 255); rgb.rgbtGreen = rgb.rgbtRed; rgb.rgbtRed = rgb.rgbtRed; return; } double f; double p; double q; double t; int nHi; nHi = (int)(dH / 60) % 6; if(nHi < 0) nHi *= -1; //dHが負のときの対策 f = dH / 60 - nHi; p = dV * (1 - dS); q = dV * (1 - f * dS); t = dV * (1 - (1 - f) * dS); //計算結果のR,G,Bは0.0~1.0なので255倍 dV = dV * 255; p = p * 255; q = q * 255; t = t * 255; if(dV > 255) dV = 255; if(t > 255) t = 255; if(p > 255) p = 255; if(q > 255) q = 255; if(nHi == 0) { rgb.rgbtRed = (BYTE)dV; rgb.rgbtGreen = (BYTE)t; rgb.rgbtBlue = (BYTE)p; return; } if(nHi == 1) { rgb.rgbtRed = (BYTE)q; rgb.rgbtGreen = (BYTE)dV; rgb.rgbtBlue = (BYTE)p; return; } if(nHi == 2) { rgb.rgbtRed = (BYTE)p; rgb.rgbtGreen = (BYTE)dV; rgb.rgbtBlue = (BYTE)t; return; } if(nHi == 3) { rgb.rgbtRed = (BYTE)p; rgb.rgbtGreen = (BYTE)q; rgb.rgbtBlue = (BYTE)dV; return; } if(nHi == 4) { rgb.rgbtRed = (BYTE)t; rgb.rgbtGreen = (BYTE)p; rgb.rgbtBlue = (BYTE)dV; return; } //if(nHi == 5) //{ rgb.rgbtRed = (BYTE)dV; rgb.rgbtGreen = (BYTE)p; rgb.rgbtBlue = (BYTE)q; return; //} } LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { CPaintDC dc(m_hWnd); //////////////////////////// //GDI+初期化 //本当はアプリケーションの初期化時に行うべきだがソースコードの簡易化のためOnPaintに配置 ULONG_PTR _nToken; Gdiplus::GdiplusStartupInput _sGdiplusStartupInput; Gdiplus::GdiplusStartup(&_nToken,&_sGdiplusStartupInput,NULL); //////////////////////////// //画像読み込み //ソースコード簡易化のため描画毎に読み出す Gdiplus::Bitmap* pImage; Gdiplus::Graphics cGraphics(dc.m_hDC); pImage = Gdiplus::Bitmap::FromFile(L"c:\\test.jpg",TRUE); //////////////////////////// //画像表示(画像処理前) cGraphics.DrawImage(pImage,(Gdiplus::REAL)0,(Gdiplus::REAL)0 ,(Gdiplus::REAL)pImage->GetWidth(),(Gdiplus::REAL)pImage->GetHeight()); //////////////////////////// //画像処理 // UINT x; UINT y; UINT nPos; RGBTRIPLE* pTmp; Gdiplus::BitmapData bitmapData; pImage->LockBits(&Gdiplus::Rect(0,0,pImage->GetWidth(),pImage->GetHeight()) ,Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite,PixelFormat24bppRGB,&bitmapData); pTmp = (RGBTRIPLE*)bitmapData.Scan0; { struct HSV { double dH; double dS; double dV; }; HSV* pHSV; pHSV = new HSV[pImage->GetWidth() * pImage->GetHeight()]; //////////////////////////////////// //RGB->HSV変換 //ソースコードが分かりやすいようにforを3箇所に分離している // nPos = 0; for(y = 0; y < pImage->GetHeight(); y++) { for(x = 0; x < pImage->GetWidth(); x++, nPos++) { RGBtoHSV(pTmp[nPos],pHSV[nPos].dH,pHSV[nPos].dS,pHSV[nPos].dV); } } //////////////////////////////////// //輝度(V)アップ // nPos = 0; for(y = 0; y < pImage->GetHeight(); y++) { for(x = 0; x < pImage->GetWidth(); x++, nPos++) { pHSV[nPos].dV += 0.2; if(pHSV[nPos].dV > 1) pHSV[nPos].dV = 1.0; } } //////////////////////////////////// //HSV->RGB変換 // nPos = 0; for(y = 0; y < pImage->GetHeight(); y++) { for(x = 0; x < pImage->GetWidth(); x++, nPos++) { HSVtoRGB(pHSV[nPos].dH,pHSV[nPos].dS,pHSV[nPos].dV,pTmp[nPos]); } } delete pHSV; } pImage->UnlockBits(&bitmapData); //////////////////////////// //画像表示(画像処理後) cGraphics.DrawImage(pImage,(Gdiplus::REAL)pImage->GetWidth() + 2,(Gdiplus::REAL)0 ,(Gdiplus::REAL)pImage->GetWidth(),(Gdiplus::REAL)pImage->GetHeight()); //////////////////////////// //画像開放 delete pImage; //////////////////////////// //GDI+開放処理 //本当はアプリケーションの終了時に行うべきだがソースコードの簡易化のため行わない //Gdiplus::GdiplusShutdown(_nToken); return 0; } };
