🎨

GoCVでガンマ補正してみた

2022/09/18に公開

こんにちは、わたる です。

初めに

GoCVを使ったガンマ補正のサンプルプログラムを探してみたのですが、自分の検索能力が低かったせいか見つからなかったので作ってみました。

考え方

GoCV、否、OpenCVにも言えることと思いますが、ガンマ補正を行うAPIは存在しませんので、変換式をCVのLUP(LookUpTable)の仕組みを用意して変換をかける方法です。

ガンマ補正の変換式

探すと色々と出てくると思いますが、入力のx座標、y座標の画素要素をI_{in}(x, y)、出力の画素要素をI_{out}(x, y)、画素が取れる最大値をI_{max}、ガンマ補正値を\gammaとすると、下記の式が一般的に使われているようです。

I_{out}(x, y) = I_{max}\times\Bigl(\frac{I_{in}(x, y)}{I_{max}}\Bigr)^{1/\gamma}

I_{max}は1画素8bitで考えて255を用いることが一般的なので、この数式は下記のような表現をされていることもあります。

I_{out}(x, y) = 255\times\Bigl(\frac{I_{in}(x, y)}{255}\Bigr)^{1/\gamma}

この変換式をそのまま0~255の出力のLUTを作って入力の値から変換させればよい…という考え方になります。

GoCVのセットアップ

前回の拙記事をご参照ください。

プログラムソース

gamma.go
package main

import (
	"fmt"
	"os"
	"math"
	"gocv.io/x/gocv"
	"strconv"
)

func main() {
	if len(os.Args) < 3 {
		fmt.Printf("How to run:\n\tgamma gamma_value imgfile")
		fmt.Printf("Ex) gamma 1.5 sample.png")
		return
	}

	arg_str_gamma := os.Args[1]
	gamma, err := strconv.ParseFloat(arg_str_gamma, 64)
	if err != nil {
		fmt.Printf("Error gamma value: %v\n", arg_str_gamma)
		return
	}
	arg_str_filename := os.Args[2]
	img := gocv.IMRead(arg_str_filename, gocv.IMReadColor)
	if img.Empty() {
		fmt.Printf("Error reading image from: %v\n", arg_str_filename)
		return
	}

	str_after := fmt.Sprintf("after gamma:%v", gamma)
	window := gocv.NewWindow("before")
	window_2 := gocv.NewWindow(str_after)

	window.IMShow(img)
	img_gamma := myGamma_conversion(img, gamma)
	defer img_gamma.Close()
	for {
		window_2.IMShow(img_gamma)
		if window_2.WaitKey(1) >= 0 {
			break
		}
	}
}

func myGamma_conversion(src gocv.Mat, gamma float64) gocv.Mat {
	gamma_cvt := gocv.NewMatWithSize(256, 1, gocv.MatTypeCV8U)
	for r := 0 ; r < 256 ; r++ {
		gamma_element := 255.0 * math.Pow(float64(r)/255.0, 1.0/gamma)
		gamma_cvt.SetUCharAt(r, 0, uint8(gamma_element))
	}
	ret_gamma := src.Clone()
	gocv.LUT(src, gamma_cvt, &ret_gamma)

	return ret_gamma
}

変換テーブルを作っているのは

for r := 0 ; r < 256 ; r++ {
    gamma_element := 255.0 * math.Pow(float64(r)/255.0, 1.0/gamma)
    gamma_cvt.SetUCharAt(r, 0, uint8(gamma_element))
}

この箇所で、
LUTとして変換しているのは

gocv.LUT(src, gamma_cvt, &ret_gamma)

この箇所です。
プログラム引数の2つ目をガンマ値、3つ目を入力ファイルとして設定していますので、例えば、

$ go run gamma.go 1.5 samplg.png

のように動作することでガンマ補正前後の画像を表示するようにしています。[1]

ガンマ補正値を1.5した出力結果は下記です。
※画像は「あの」レナ様です。

ガンマ値1.5
ガンマ値を1.5としたときの出力結果

サンプルのガンマ値を0.667にした出力結果は下記です。
ガンマ値0.667
ガンマ値を0.667としたときの出力結果

ガンマ値が1より大きいと明るく、1より小さいと暗く変換するのが分かるかと思います。

まとめ

今回はGoCVを使ったガンマ補正のプログラムを作ってみました。

この記事も前回と同様qiitaからの転載[2]になりますが、他にもヒストグラムを作ってみたので、またこれも転載するとともに、新たに他の画像変換やgolangのプログラムも作ってみてアップしたいと思います。

脚注
  1. 引数チェックが甘いかもしれませんが、そこは野良ソースということで…。 ↩︎

  2. qiitaは色々あったのでどうしようか…と思い、結果、こちらに移行しようとしています。そのため、qiitaのURLについては記載しません。 ↩︎

Discussion