質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Q&A

2回答

1825閲覧

OpenCVのHSVカラー空間

kojihaya

総合スコア0

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

0グッド

0クリップ

投稿2021/10/05 08:41

前提・実現したいこと

OpenCVのHSVカラーで色変化を手動で操作しようとしているのですが、
H,S,Vは各8ビットで、
H=0<ー>180
S=0<ー>255
V=0<ー>255
の範囲ですが、
SV色空間は底辺V、高さSの正方形の空間ではないようです。
この範囲で変化させていくと、白い色がピンク色になったりします。
底辺V高さSの三角形内でないといろは破綻するみたいです。
OpenCVでは
https://www.oh-benri-tools.com/tools/color/hsl-hsv-color-picker
の正方形のSV空間ではなく、
https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Hsv_sample.png
この図のよう三角形のSV空間が正しいのでしょうかね?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/10/05 09:10

(R,G,B)の全パータンについて, OpenCVの用いている変換式でHSVに変換してみて, その結果のSVの分布を見ればわかるのではないでしょうか. (そういうコードをちょろっと書いてみればどうでしょう)
guest

回答2

0

「質問への追記・修正の依頼」に書いた

(R,G,B)の全パータンについて,
OpenCVの用いている変換式でHSVに変換してみて,
その結果のSVの分布を見ればわかるのではないでしょうか.

をやってみました.

  • 1x1 の画像を作って,forループで色を変えては cv::cvtColor でHSVに変換させ,結果のSとVをプロットした.
  • せっかくなので,BGR→HSV→BGR と再変換したらどの程度の誤差が生じるのかも調べてみた.

C++

1int main() 2{ 3 cv::Mat BGR( 1,1, CV_8UC3 ); 4 cv::Mat HSV( 1,1, CV_8UC3 ); 5 cv::Mat Result = cv::Mat::zeros( 256,256, CV_8U ); 6 unsigned int Diffs[ 10 ] = { 0 }; 7 8 for( int r=0; r<=255; ++r ) 9 { 10 for( int g=0; g<=255; ++g ) 11 { 12 for( int b=0; b<=255; ++b ) 13 { 14 BGR = cv::Scalar(b,g,r); 15 cv::cvtColor( BGR, HSV, cv::COLOR_BGR2HSV ); 16 const auto &hsv = HSV.at< cv::Vec3b >( 0,0 ); 17 Result.at< unsigned char >( hsv[1], hsv[2] ) = 255; 18 19 //逆変換 20 cv::cvtColor( HSV, BGR, cv::COLOR_HSV2BGR ); 21 const auto &bgr = BGR.at< cv::Vec3b >( 0,0 ); 22 int diff = std::min( abs( b-bgr[0] ) + abs( g-bgr[1] ) + abs( r-bgr[2] ), 9 ); 23 ++Diffs[diff]; 24 } 25 } 26 } 27 28 for( int i=0; i<10; ++i ) 29 { 30 std::cout << i << " : " << Diffs[i] << std::endl; 31 } 32 33 cv::imshow( "Result", Result ); 34 if( cv::waitKey() == 's' )cv::imwrite( "Result.png", Result ); 35 36 return 0; 37}

以下に,結果のスクリーンショットを示す.
左側の数値の表示は,再変換後の誤差(「市街地距離」というのかな)の度数分布.
正方形のが画像は 256x256 のS-V 空間に結果をプロットしたやつ(横軸がV,縦軸がS.黒地に白でプロットした).

  • とりあえず S-V 空間全域にわたって相応に点が散らばっている.
  • 再変換後の誤差の最大値は5であった.

(つまり,少なくとも白でプロットした位置については「それなりに真っ当に」BGRに変換できる(=急に突拍子もない色になったりはしない).

…という感じ.
以上より,

三角形内でないといろは破綻する

という感はない.
(「正方形」と言えるのでは?)

イメージ説明

投稿2021/10/05 10:31

fana

総合スコア11708

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kojihaya

2021/10/05 10:49

素晴らしい調査ありがとうぞございます。 そのようですね。私も再調査してみます。 ありがとうございました。(__)
kojihaya

2021/10/06 03:25 編集

RGBからのHSVでは大丈夫ですが、HSVデータ内で加工するとダメなようでした。 ご足労お掛けしました。
guest

0

RGBからHSVへの変換では大丈夫ですが、HSVデータ内で操作するとやはり三角みたいですが、どうでしょう。テストでKey’1’押下でS(彩度)を全部100とすると崩れます。以外はV(明度)の半分の値をSに入れてみました。

int main()
{
cv::Mat img = cv::imread("test_HSV.jpg");

cv::Mat hsv; cv::Mat img2; cv::imshow("Test img", img); int key = cv::waitKey(); cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV); for (int y = 0; y < hsv.rows; y++) { for (int x = 0; x < hsv.cols; x++) { cv::Vec3b p = hsv.at<cv::Vec3b>(cv::Point(x, y)); if (key == '1') { p[1] = 100; // S } else { if (p[2] < 128) { // v 0<->128<->255 p[1] = p[2] / 2; // S } else { p[1] = (255 - p[2]) / 2; // S } } hsv.at<cv::Vec3b>(cv::Point(x, y)) = p; } } cv::cvtColor(hsv, img2, cv::COLOR_HSV2BGR); cv::imshow("Test img 2", img2); cv::waitKey(10000); return 0;

}
イメージ説明

投稿2021/10/05 13:06

kojihaya

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/10/05 14:33 編集

彩度を一律に100にしたら、本来彩度が0に近く色相の影響をほぼ受けない白い部分の連続性がおかしくなるのは当然です.普通は0.5倍等、線形的に変換します.
退会済みユーザー

退会済みユーザー

2021/10/05 14:35 編集

理解できなければ試しに、https://www.peko-step.com/tool/hsvrgb.html で(r,g,b)=(255,255,254), (255,254,255), (254,255,255)をそれぞれS=100に変えてみてください
kojihaya

2021/10/05 23:42 編集

なるほど、黄色になりますね。 私がやりたかったことは、写真などを明暗調整するプログラムを作りたかったのです。 勉強不足で単純にS,Vをそれぞれ変化させればよいのかと思ってしまったのです。 画素単位にS,Vをそれぞれ+-100%まで変化させる計算式をを知っていたらどなたかご伝授ください。 私も考えてみます。
fana

2021/10/06 01:12

「明暗」を調整するならば,いじくるべき主たる対象は(Sではなく) V でしょう. とりあえず CVの8bitデータでの値域としては V の範囲は 0~100 なので, 入力V (0~100の範囲) を 処理結果 V' (0~100の範囲) に変換するような何らかの式 V' = f( V ) を用意して変換してやるというのが一般的(?)な話となるでしょう. よく用いられる話としては,「ガンマ補正」とか「ヒストグラム平坦化」などについて調べてみると良いかもしれません. 方法を検索するとしたら「画像 輝度 補正」みたいな感じかな?
fana

2021/10/06 01:15

で,そんな感じで V だけいじくると,見た目に色味の雰囲気(過不足,というか)が変わったりするので, 「Sの方もちょっと何か調整した方がいいんじゃないの?」とか思えてきたりするのですが, そこでS側をどうすればいい感じになるのか? という話に関しては私は知りません. なので,そこらへんでいい感じの話を見つけられた場合には教えていただきたく.
jbpb0

2021/10/06 05:16

eduidlさんが紹介したWebページで、RGBに適当に数字を入れてから、Vを変えたらRGBの比が保たれたまま増減することが分かります たとえば、R=200, G=100としてからVを変えたら、RはGの2倍のままで値が変わります (たまに1ずれたりはしますが) ただし、上記のように比例倍していいのは線形のデータの場合で、JPEG等の一般的な画像データはガンマ補正された非線形データなので、そのままでは比例倍できません そこで、下記の手順を行うことになると思います ・RGBを線形化 ・HSVに変換 ・V変える ・RGBに変換 ・RGBをガンマ補正
kojihaya

2021/10/06 06:49

みなさま、ありがとうございます。 参考にさせていただき、やってみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問