OpenCV 画像の色味をHSVで変更してみる。

#はじめに
カラー画像の色空間はBGR,RGBが通常ですが、RGB色空間だけでは処理が厳しい場合もあり、
その場合、HSV色空間を使ったりします。

**HSVカラーリング**
![HSV_カラーリング.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/d23988b5-6e23-2a09-6038-c1c9237d6b13.png)

HSV色空間を構成する色相(H),彩度(S),明度(V)がそれぞれどのように機能するのか？
それぞれ１つを変化させ、その画像を見たいと思います。
HSV色空間について、記事が少なかったので、ニーズがあるかどうかは
分かりませんが、書いてみようかと思いました。
OpenCVとPythonで行ってみたいと思います。


#行うこと
事前にOpenCVをインストールしておく必要があります。
読み込んだBGRカラー画像をHSV色空間に変換します。
H,S,Vを１つづつ変更し、画像を変更画像を作成、表示します。


##色相(H)の変更
HSVのHの変更は値を加算するのみです。
HSVのHは角度で循環するので、計算値がオーバーまたはアンダーした場合は
範囲におさまるように修正します。
範囲は0～255で、計算途中でfloat32に型変換を行います。

##彩度(S),明度(V)の変更
彩度(S),明度(V)は上下限があり、計算は共通changedSV()を使います。

S ← S × alpha + beta
V ← V × alpha + beta

##BGR-HSV色空間の変換
OpenCVのcvtColor()関数を使います。
オプションは、COLOR_BGR2HSV_FULL, COLOR_HSV2BGR_FULLと
...._FULLの付いたものを使います。
色相(H)の階調が180階調から256階調になるからです。

cvtColor()の詳細は下記をご覧ください。
[docs.opencv.org - Color Space Conversions](https://docs.opencv.org/4.5.1/d8/d01/group__imgproc__color__conversions.html#ga397ae87e1288a81d2363b61574eb8cab)


#実行
##初期

```python
import numpy as np
import cv2

# HSV S(彩度),V(明度)の変更
def changedSV(bgr_img, alpha, beta, color_idx):
    hsvimage = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV_FULL) # BGR->HSV
    hsvf = hsvimage.astype(np.float32)
    hsvf[:,:,color_idx] = np.clip(hsvf[:,:,1] * alpha+beta, 0, 255)
    hsv8 = hsvf.astype(np.uint8)
    return cv2.cvtColor(hsv8,cv2.COLOR_HSV2BGR_FULL)

bgr_img = cv2.imread('colorfull_tulips.jpg', cv2.IMREAD_COLOR)
```

まず、初期コードとしてこのようなコードを実行します。
changedSV()は、S(彩度),V(明度)変換共通のコードです。


##入力画像
**読み込まれHSV色空間で処理される画像**
![colorfull_tulips.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/950772ab-1b92-fc06-f306-f6ef664a2cac.jpeg)
このカラフルなチューリップ画像を用います。


##色相(H)の変更
###HSV 色相(H)の変更サンプルコード

```python
# HSV H(色相)の変更
def changedH(bgr_img, shift):
    hsvimage = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV_FULL) # BGR->HSV
    hi = hsvimage[:,:,0].astype(np.int32)
    if shift < 0:
        nhi = hi.flatten()
        for px in nhi:
            if px < 0:
                px = 255 - px
        nhi = nhi.reshape(hsvimage.shape[:2])
        hi = nhi.astype(np.uint8)
    chimg = (hi + shift) % 255
    hsvimage[:,:,0] = chimg
    hsv8 = hsvimage.astype(np.uint8)
    return cv2.cvtColor(hsv8,cv2.COLOR_HSV2BGR_FULL) # HSV->BGR

# HSV S(彩度)の変更
image_h1 = changedH(bgr_img, 85)
image_h2 = changedH(bgr_img, -85)
```

**色相(H)変更条件 120°(85) 加算**
![HSV_h1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/f69926d1-2f1e-f066-7b1c-c28ea66fda03.jpeg)
黄色(H=60°)のチューリップが、シアン色(H=180°)に変化しています。

**色相(H)変更条件 -120°(-85) 加算**
![HSV_h2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/8d54c7e4-0b8b-cbdb-26c4-aa5b059973ae.jpeg)
黄色(H=60°)のチューリップが、マゼンタ色(H=300°)に変化しています。
異世界の画像の様です。微妙なカメラ調整とか、特殊効果には使えますが、
マスク処理と組み合わせないと、なかなか使い道がなさそうです。


##彩度(S)の変更結果
###HSV 彩度(S)の変更サンプルコード

```python
# HSV S(彩度)の変更
def changedS(bgr_img, alpha, beta):
    return changedSV(bgr_img, alpha, beta, 1)

# HSV S(彩度)の変更
image_s1 = changedS(bgr_img, 2.0, -20)
image_s2 = changedS(bgr_img, 1/2, 20)
```

bgr_imgはBGR 24bitカラー画像です。
changedS()の返却値は変更されたBGR 24bitカラー画像です。

**彩度(S)変更条件 alpha=2.0 beta=-20**
![HSV_s1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/123cffd7-4cf4-ba54-89ca-e9cf4725c35b.jpeg)
彩度(S)を上げると色が濃くなりました。

**彩度(S)変更条件 alpha=0.5 beta=20**
![HSV_s2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/c03813d3-6cf4-51fa-902d-5d5302f5b62c.jpeg)
彩度(S)を下げると色が薄くなります。

##明度(V)の変更
###HSV 明度(V)の変更サンプルコード

```python
# HSV V(明度)の変更
def changedV(bgr_img, alpha, beta):
    return changedSV(bgr_img, alpha, beta, 2)

# HSV V(明度)の変更
image_v1 = changedV(bgr_img, 1.8, 20)
image_v2 = changedV(bgr_img, 1/2, -50)
```

bgr_imgはBGR 24bitカラー画像です。
changedV()の返却値は変更されたBGR 24bitカラー画像です。


**明度(V)変更条件 alpha=1.8 beta=20**
![HSV_v1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/19595d2f-c50b-734e-7774-f36925a9076c.jpeg)
明度を上げたら、画像が明るくなります。

**明度(V)変更条件 alpha=0.5 beta=-50**
![HSV_v2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/3f9a1d29-3a01-5a9e-5459-7d65bacb531e.jpeg)
明度を下げたら、画像が暗くなります。


#Pythonサンプルプログラム

```python
import numpy as np
import cv2

# HSV H(色相)の変更
def changedH(bgr_img, shift):
    hsvimage = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV_FULL) # BGR->HSV
    hi = hsvimage[:,:,0].astype(np.int32)
    if shift < 0:
        nhi = hi.flatten()
        for px in nhi:
            if px < 0:
                px = 255 - px
        nhi = nhi.reshape(hsvimage.shape[:2])
        hi = nhi.astype(np.uint8)
    chimg = (hi + shift) % 255
    hsvimage[:,:,0] = chimg
    hsv8 = hsvimage.astype(np.uint8)
    return cv2.cvtColor(hsv8,cv2.COLOR_HSV2BGR_FULL) # HSV->BGR

# HSV S(彩度),V(明度)の変更
def changedSV(bgr_img, alpha, beta, color_idx):
    hsvimage = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV_FULL) # BGR->HSV
    hsvf = hsvimage.astype(np.float32)
    hsvf[:,:,color_idx] = np.clip(hsvf[:,:,1] * alpha+beta, 0, 255)
    hsv8 = hsvf.astype(np.uint8)
    return cv2.cvtColor(hsv8,cv2.COLOR_HSV2BGR_FULL)

# HSV S(彩度)の変更
def changedS(bgr_img, alpha, beta):
    return changedSV(bgr_img, alpha, beta, 1)

# HSV V(明度)の変更
def changedV(bgr_img, alpha, beta):
    return changedSV(bgr_img, alpha, beta, 2)

# 画像読み込み
bgr_img = cv2.imread('colorfull_tulips.jpg', cv2.IMREAD_COLOR)

# HSV S(彩度)の変更
image_h1 = changedH(bgr_img, 85)
image_h2 = changedH(bgr_img, -85)

# HSV S(彩度)の変更
image_s1 = changedS(bgr_img, 2.0, -20)
image_s2 = changedS(bgr_img, 1/2, 20)

# HSV V(明度)の変更
image_v1 = changedV(bgr_img, 1.8, 20)
image_v2 = changedV(bgr_img, 1/2, -50)

# 画像表示
cv2.imshow('original', bgr_img)
cv2.imshow('HSV h1', image_h1)
cv2.imshow('HSV h2', image_h2)
cv2.imshow('HSV s1', image_s1)
cv2.imshow('HSV s2', image_s2)
cv2.imshow('HSV v1', image_v1)
cv2.imshow('HSV v2', image_v2)
cv2.waitKey(0)
```


#動作環境
Windows10 Anaconda 3 (Miniforge)
Python 3.9.2 packaged by conda-forge
OpenCV 4.5.1
numpy 1.20.1


#さいごに
HSVについてはキーエンス XGシリーズの画像処理装置にもサポートされていたと
おもいます。産業分野でもカラー画像を処理することが増えてきていますので
BGR、RGB以外の色空間を知ることも必要になってくるかと思います。
さて、HSVの画像変化について、その変化を説明しなければと思って付けてみましたが、
あの程度の説明なら不要感がありますね。
お読みいただきありがとうございました。


#参考
[Emotion Explorer - OpenCV 花の色を変えてみる。HSV色空間で色相を操作](https://emotionexplorer.blog.fc2.com/blog-entry-331.html)
[Emotion Explorer - OpenCV HSV色空間で彩度、明度を変更](https://emotionexplorer.blog.fc2.com/blog-entry-332.html)


#写真引用
「カラフルチューリップ」 [photoAC](https://www.photo-ac.com/)掲載 [リトルマロン](https://www.photo-ac.com/profile/3228465)さんの写真