OpenCVを使用したシンプルなグラフィカルエディター

この記事では、opencvコンピュータービジョンライブラリを使用してC ++で画像エディターをすばやく簡単に作成する方法を説明します。 彩度、露出、シャープネス、コントラストなどの実装された効果。 魔法じゃない!



画像



注意! 猫の下にはたくさんのグラフィックとコードがあります。



それでは始めましょう...



彩度



成分

-HSVカラーシステム、

-レイヤーに分割する機能「分割」、

-レイヤーをマージする機能は「マージ」します。



彩度を変更するには、画像をHSVカラーシステムに変換し、レイヤーに分割します。 「Sature」レイヤーの値にステップが追加されます。 レイヤーがマージされます。 すべてがシンプルです:



彩度
void CImageEditor::Sature(int step) { try { std::vector<Mat> hsv; cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL); cv::split(*m_imgEdit, hsv); hsv[1] += step * 5; cv::merge(hsv, *m_imgEdit); cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL); } catch (Exception ex) { } }
      
      







に後



博覧会



成分

-HSVカラーシステム、

-関数「分割」、「マージ」、およびヒストグラム「LUT」による変換関数、

-関数x + sin(x * 0.01255)*ステップ* 10によって変換されたヒストグラム

-ヒストグラムのバイト値のオーバーフローに対する保護。

彩度の場合と同様に、画像はHSVに変換され、レイヤーに分割されます。 「値」レイヤーでは、関数i + sin(i * 0.01255)*ステップ* 10で定義されるヒストグラムを使用して変換を実行します。同時に、バイト数のオーバーフローから自分自身を保護することを忘れないでください。

博覧会
 void CImageEditor::Expo(int step) { try { std::vector<Mat> hsv; cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL); Mat lut = GetGammaExpo(step); cv::split(*m_imgEdit, hsv); cv::LUT(hsv[2], lut, hsv[2]); cv::merge(hsv, *m_imgEdit); cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL); } catch (Exception ex) { } } cv::Mat CImageEditor::GetGammaExpo(int step) { Mat result(1, 256, CV_8UC1); uchar* p = result.data; for (int i = 0; i < 256; i++) { p[i] = AddDoubleToByte(i, std::sin(i * 0.01255) * step * 10); } return result; } byte CImageEditor::AddDoubleToByte(byte bt, double d) { byte result = bt; if (double(result) + d > 255) result = 255; else if (double(result) + d < 0) result = 0; else { result += d; } return result; }
      
      







に後

関数グラフx + sin(x * 0.01255)*ステップ* 10

画像

この機能は主に範囲の中央に影響します。



色相



成分

-RGBカラーシステム、

-機能「分割」、「マージ」および「LUT」、

-赤、青、緑のチャンネルの露出関数によって変換されたヒストグラム、

-ヒストグラム値のオーバーフローに対する保護。



色相パラメータは、緑と紫の画像での存在を特徴付けます。 RGBカラーシステムでは、緑のレイヤーを制御できますが、他の2つのレイヤーの輝度の低下を補正することを忘れないでください。 赤と青のレイヤーを変換するには、正の露出ガンマ関数が使用され、緑に対して-負のガンマが使用されます。



色相
 void CImageEditor::Hue(int step) { try { std::vector<Mat> rgb; Mat lut0 = GetGammaExpo(step), lut1 = GetGammaExpo(-step), lut2 = GetGammaExpo(step); cv::split(*m_imgEdit, rgb); LUT(rgb[0], lut0, rgb[0]); LUT(rgb[1], lut1, rgb[1]); LUT(rgb[2], lut2, rgb[2]); cv::merge(rgb, *m_imgEdit); } catch (Exception ex) { } }
      
      







に後



色温度



成分:日陰の場合と同じですが、赤と緑のヒストグラムは正であり、青のレイヤーのヒストグラムは二重負です。



色温度は、画像内の黄色と青色の存在を特徴付けます。 そこで、青を「ねじり」ます。



色温度
 void CImageEditor::Temperature(int step) { try { std::vector<Mat> rgb; Mat lut0 = GetGammaExpo(-step*2), lut1 = GetGammaExpo(step), lut2 = GetGammaExpo(step); cv::split(*m_imgEdit, rgb); LUT(rgb[0], lut0, rgb[0]); LUT(rgb[1], lut1, rgb[1]); LUT(rgb[2], lut2, rgb[2]); cv::merge(rgb, *m_imgEdit); } catch (Exception ex) { } }
      
      







に後



光と影



成分

-HSVカラーシステム、

-機能「分割」、「マージ」、「LUT」、

-関数によって変換された影のヒストグラム(0.36811145 * e)^(-(x ^ 1.7))* 0.2x *ステップ、

-関数によって変換されたライトのヒストグラム(0.36811145 * e)^(-(256-x)^ 1.7)* 0.2(256-x)*ステップ、

-ヒストグラム値のオーバーフローに対する保護。



「明るい」パラメータは画像の明るい領域の明るさを特徴づけ、「影」パラメータは暗い領域の明るさを特徴づけます。 明るさのチャンネルを変換します。



<img src = " 「alt = "画像" />



グラフでは、シャドウ変換関数は赤い線で示され、ライト関数は緑の線で示されます。



光と影
 void CImageEditor::White(int step) { try { std::vector<Mat> hsv; cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL); cv::split(*m_imgEdit, hsv); Mat lut = GetGammaLightShadow(step, true); LUT(hsv[2], lut, hsv[2]); cv::merge(hsv, *m_imgEdit); cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL); } catch (Exception ex) { AfxMessageBox(CString(CStringA(ex.msg.begin()))); throw; } } void CImageEditor::Shadow(int step) { try { std::vector<Mat> hsv; cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_RGB2HSV_FULL); cv::split(*m_imgEdit, hsv); Mat lut = GetGammaLightShadow(step, false); LUT(hsv[2], lut, hsv[2]); cv::merge(hsv, *m_imgEdit); cv::cvtColor(*m_imgEdit, *m_imgEdit, cv::ColorConversionCodes::COLOR_HSV2RGB_FULL); } catch (Exception ex) { AfxMessageBox(CString(CStringA(ex.msg.begin()))); throw; } } Mat CImageEditor::GetGammaLightShadow(int step, bool reverse) { Mat result(1, 256, CV_8UC1); for (int i = 0; i < 256; i++) { *(result.data + i) = AddDoubleToByte(i, std::pow(0.36811145*M_E, -std::pow(abs((reverse ? 256 : 0) - i), 1.7))*0.2*step*abs((reverse ? 256 : 0) - i)); } return result; }
      
      







光影



コントラスト



成分

-RGBカラーシステム、

-機能「分割」、「マージ」、「LUT」、

-コントラストレベル「(100 +ステップ)/ 100」、

-式((x / 255-0.5)* constrastLevel + 0.5)* 255から取得したコントラストヒストグラム



コントラストは、明るさの違いによって決まります。 つまり コントラストを上げるには、中心から端まで明るさの範囲を広げる必要があります。 変換はすべてのレイヤーに対して実行されます。



コントラスト
 void CImageEditor::Contrast(int step) { try { std::vector<Mat> rgb; cv::split(*m_imgEdit, rgb); Mat lut(1, 256, CV_8UC1); double contrastLevel = double(100 + step) / 100; uchar* p = lut.data; double d; for (int i = 0; i < 256; i++) { d = ((double(i) / 255 - 0.5)*contrastLevel + 0.5) * 255; if (d > 255) d = 255; if (d < 0) d = 0; p[i] = d; } LUT(rgb[0], lut, rgb[0]); LUT(rgb[1], lut, rgb[1]); LUT(rgb[2], lut, rgb[2]); cv::merge(rgb, *m_imgEdit); } catch (Exception ex) { AfxMessageBox(CString(CStringA(ex.msg.begin()))); throw; } }
      
      







画像



赤い線はコントラストが高く、緑の線は低くなっています。



に後



シャープネス



成分

-ぼかし機能「ぼかし」、

-計算された係数を持つ畳み込み行列、

-畳み込み行列変換関数「filter2D」、

-画像のコピー。



シャープネス(シャープネス)は、個々の要素の選択、輪郭によって決まります。 シャープネスの逆はぼかしです。

opencvでは、ぼかし関数を使用して画像をぼかします。この関数は、ソース画像、出力画像、およびぼかし行列のサイズをパラメーターとして取ります。 ぼかしの強さは、ぼかしマトリックスのサイズに依存します。 このサイズは、マトリックスの中心を手動で示さないように均一でなければなりません。



opencvの明瞭度は、畳み込み行列を使用して増加させるのが最も簡単であり、このために特別な行列を使用します。 元の画像、結果の画像、畳み込み行列値ごとのビット数、畳み込み行列を受け取るfilter2D関数は、直接変換を実行します。 したがって、アップ/ダウン方法はどのようになりますか。



シャープネス
 void CImageEditor::Clarity(int step) { try { if (step < 0) { cv::blur(*m_imgEdit, *m_imgEdit, cv::Size(-step * 2 + 1, -step * 2 + 1)); } else { Mat dst = m_imgEdit->clone(); float matr[9] { -0.0375 - 0.05*step, -0.0375 - 0.05*step, -0.0375 - 0.05*step, -0.0375 - 0.05*step, 1.3 + 0.4*step, -0.0375 - 0.05*step, -0.0375 - 0.05*step, -0.0375 - 0.05*step, -0.0375 - 0.05*step }; Mat kernel_matrix = Mat(3, 3, CV_32FC1, &matr); cv::filter2D(*m_imgEdit, dst, 32, kernel_matrix); m_imgEdit = make_shared<Mat>(dst); } } catch (Exception ex) { AfxMessageBox(CString(CStringA(ex.msg.begin()))); throw; } }
      
      







に後



まとめ



ほとんど魔法はありません。 まあ、魔法の数は経験的に見つけられるので、それらの代わりに、あなた自身の、最も適切なものを使うことができます。

デモアプリケーションへのリンク。



All Articles