OpenCV で画像の色情報をヒストグラムにして表示する

色情報をヒストグラムにするために、OpenCV の cv2.calcHist が便利だった。第一引数に画像情報を渡し、ヒストグラムにすることができる。

このヒストグラムを表示するために、 np.empty((256, 256, 3), np.uint8) のように表示領域を作り、 cv2.line で線を引いた。また、 HSV 空間でのH(色相)と数値の対応を覚えていないので、hsvcol という関数を定義し、 cv2.line で引く線の色を設定した。

出力したヒストグラムつき動画

右端に4つのヒストグラムを連結した。ヒストグラムは上から順に、RGB、H(色相)、S(彩度)、V(明るさ)の分布を示している。

ヒストグラムを作成するコード

import cv2
import sys
import numpy as np
import moviepy.editor as mp

# 画像を加工する
def mkimg(img):
    resized_his = cv2.resize(mkhispic(img),(256,height))
    cv2.imshow("Frameaa", resized_his)    
    return cv2.hconcat([img,resized_his])

# ヒストグラム用
c_rgb = np.empty((256, 256, 3), np.uint8)
color = ((255, 0, 0), (0, 255, 0), (0, 0, 255))
c_hsv = np.empty((3, 256, 256, 3), np.uint8)

# ヒストグラム描画
def mkhispic(img):
    hsvframe = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
    c_rgb.fill(255)
    c_hsv.fill(255)
    for channel in range(3):
        # ヒストグラムを計算
        h_rgb = cv2.calcHist([frame], [channel], None, [256], [0, 256])
        h_hsv = cv2.calcHist([hsvframe], [channel], None, [256], [0, 256])
        # 折れ線の開始位置(左下)
        prev_xy_rgb = (0, 255)
        for x in range(256):
            # 座標を取得し、線を引く
            current_xy_rgb = (x, 255 - int(h_rgb [x] / 1000))
            current_xy_hsv = (x, 255 - int(h_hsv [x] / 1000))
            cv2.line(c_rgb, prev_xy_rgb, current_xy_rgb, color[channel])
            cv2.line(c_hsv[channel], (x, 255), current_xy_hsv, hsvcol(channel, x))           
            # 現在座標を前座標に保存
            prev_xy_rgb = current_xy_rgb
            
    # ヒストグラムにタイトルをつける
    cv2.putText(c_rgb, "RGB", (10, 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 0), 1, 4)
    cv2.putText(c_hsv[0], "H", (10, 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 0), 1, 4)
    cv2.putText(c_hsv[1], "S", (10, 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 0), 1, 4)
    cv2.putText(c_hsv[2], "V", (10, 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 0), 1, 4)    
    return cv2.vconcat([c_rgb, c_hsv[0], c_hsv[1], c_hsv[2]])

# hsvの状況を色で表現
def hsvcol(channel,v):
    if channel == 0:
        a = cv2.cvtColor(np.uint8([[[v, 255, 255]]]),cv2.COLOR_HSV2BGR)[0, 0, :]
    elif channel == 1:
        a = cv2.cvtColor(np.uint8([[[0, v, 255]]]),cv2.COLOR_HSV2BGR)[0, 0, :]
    elif channel == 2:
        a = cv2.cvtColor(np.uint8([[[0, 0, v]]]),cv2.COLOR_HSV2BGR)[0, 0, :]
    return (int(a[0]), int(a[1]), int(a[2]))

# 動画関連 -------------------------------------------------------------
def set_audio(srcfile,imgfile,outfile):
    # Extract audio from input video.
    clip_input = mp.VideoFileClip(srcfile)
    clip_input.audio.write_audiofile('audio.mp3')
    # Add audio to output video.
    clip = mp.VideoFileClip(imgfile).subclip()
    clip.write_videofile(outfile, audio='audio.mp3') 
  
# 動画のファイル名 設定
srcfile = sys.argv[1]
imgfile = "out.mp4"
outfile = sys.argv[1].replace(".mp4","out.mp4").replace(".MP4","out.MP4")
video = cv2.VideoCapture(srcfile)

# 幅と高さを取得
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
size = (width + 256, height) # ヒストグラム用にサイズを大きく

# 総フレーム数/フレームレートを取得
frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
frame_rate = int(video.get(cv2.CAP_PROP_FPS))

# 保存用
fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
writer = cv2.VideoWriter(imgfile, fmt, frame_rate, size)

for i in range(frame_count):
    ret, frame = video.read()
    write_frame = mkimg(frame)
    writer.write(write_frame)

    cv2.imshow("Frame", frame)
    cv2.imshow("newf", write_frame)
    cv2.moveWindow("newf",3700,0)
    
    # qキーが押されたら途中終了
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break

writer.release()
video.release()
cv2.destroyAllWindows()

# 最後に音をつける
set_audio(srcfile,imgfile,outfile)

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)