Pynote

Python、機械学習、画像処理について

OpenCV - 画像の統計量を計算する

概要

画像から最小値、最大値、平均値、中央値、最頻値、分散、標準偏差といった各種統計量を計算する。

試した環境

f:id:nekobean:20180614001040j:plain

今回使用した画像

ipynb は こちら

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 画像を読み込む。
img = cv2.imread("flower.jpg", cv2.IMREAD_GRAYSCALE)

統計量

最小値、最大値

画素値の中で最小のものを最小値 (minimum value)、最大のものを最大値 (maximum value) という。

関数

minMaxLoc(), minMaxIdx()

// src が2次元配列の場合、こちらで最小値、最大値及びその位置を取得できる。
// src のチャンネル数は1である必要がある。
void cv::minMaxLoc(InputArray src,
    double  minVal,
    double *maxVal = 0,
    Point *minLoc = 0,
    Point *maxLoc = 0,
    InputArray mask = noArray())
// src が n 次元配列の場合、位置を int 型の配列経由で受け取る。
void cv::minMaxIdx(InputArray src,
    double *minVal,
    double *maxVal = 0,
    int *minIdx = 0,
    int *maxIdx = 0,
    InputArray mask = noArray())
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(src[, mask])

コード

C++

#include <iostream>

#include <opencv2/opencv.hpp>

int main()
{
    // 画像を読み込む。
    cv::Mat img = cv::imread("sample.jpg", cv::IMREAD_GRAYSCALE);

    // 画像の最小値、最大値及びその位置を取得する。
    double minVal, maxVal;
    cv::Point minLoc, maxLoc;
    cv::minMaxLoc(img, &minVal, &maxVal, &minLoc, &maxLoc);

    std::cout << "minVal: " << minVal << ", minLoc: " << minLoc << std::endl;
    std::cout << "maxVal: " << maxVal << ", maxLoc: " << maxLoc << std::endl;
    // minVal: 0, minLoc: [197, 134]
    // maxVal: 252, maxLoc: [211, 106]
}
#include <iostream>

#include <opencv2/opencv.hpp>

int main()
{
    // 画像を読み込む。
    cv::Mat img = cv::imread("sample.jpg", cv::IMREAD_GRAYSCALE);

    // 画像の最小値、最大値及びその位置を取得する。
    double minVal, maxVal;
    int minIdx[2], maxIdx[2];
    cv::minMaxIdx(img, &minVal, &maxVal, minIdx, maxIdx);

    std::cout << "minVal: " << minVal << ", minIdx: " << minIdx[0] << ", " << minIdx[1]
              << std::endl;
    std::cout << "maxVal: " << maxVal << ", maxLoc: " << maxIdx[0] << ", " << maxIdx[1]
              << std::endl;
    // minVal: 0, minIdx: 134, 197
    // maxVal: 252, maxLoc: 106, 211

    // minIdx が 134, 197 となっているが、これは配列の134行197列目を意味する。
    // 位置は minMaxLoc では Point(x, y) であったが、minMaxIdx では配列のインデックス
    // となる違いに注意する。
}

Python

import cv2
import numpy as np

# 画像を読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)

# OpenCV
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(img)
print(f"minVal: {minVal}, minLoc: {minLoc}")
print(f"maxVal: {maxVal}, maxLoc: {maxLoc}")
# minVal: 0.0, minLoc: (197, 134)
# maxVal: 252.0, maxLoc: (211, 106)

# numpy
minVal = img.min()
minLoc = np.unravel_index(np.argmin(img), img.shape)
maxVal = img.max()
maxLoc = np.unravel_index(np.argmax(img), img.shape)
print(f"minVal: {minVal}, minLoc: {minLoc}")
print(f"maxVal: {maxVal}, maxLoc: {maxLoc}")
# minVal: 0, minLoc: (134, 197)
# maxVal: 252, maxLoc: (106, 211)

# numpy を使う場合、minLoc, maxLoc は (y, x) の順になっているので注意

平均値

画像サイズが M \times N としたとき、その画像の平均値 (mean value) \mu は次の式で計算できる。

\displaystyle \mu = \frac{1}{MN} \sum_{i=0}^{N-1} \sum_{j=0}^{M-1} img(i, j)

ただし、 img(i, j) は画像の位置 (i, j) の画素値を表す。

関数

mean()

Scalar cv::mean(InputArray src, InputArray mask = noArray())
retval = cv2.mean(src[, mask])

コード

C++

#include <iostream>

#include <opencv2/opencv.hpp>

int main()
{
    // 画像を読み込む。
    cv::Mat img = cv::imread("sample.jpg", cv::IMREAD_GRAYSCALE);

    // 画像の平均値を計算する。
    cv::Scalar mean = cv::mean(img);
    std::cout << "mean: " << mean[0] << std::endl; // mean: 81.1243
}
import cv2
import numpy as np

# 画像を読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)

# OpenCV
mean = cv2.mean(img)
print('mean', mean)  # mean (81.1242891566265, 0.0, 0.0, 0.0)

# numpy
mean = np.mean(img)
print('mean', mean)  # mean 81.1242891566265

中央値

画像サイズが M \times N としたとき、その画像の中央値 (median) は次の式で計算できる。

  • MN が奇数の場合: 画素値を小さい順に並べたときに MN / 2 番目の値
  • MN が偶数の場合: 画素値を小さい順に並べたときに (MN + 1) / 2 番目の値
import cv2
import numpy as np

# 画像を読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)

median = np.median(img)
print('median', median)  # median 64.0

最頻値

画素値の中で最も頻度が高い値を最頻値 (mode) という。

import cv2
import numpy as np

# 画像を読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)

from scipy import stats

mode, count = stats.mode(img.flatten())
print("mode", mode[0])  # mode 41

分散、標準偏差

画像サイズが M \times N としたとき、その画像の分散 (variance) \sigma^2 は次の式で計算できる。

\displaystyle \sigma^2 = \frac{1}{MN} \sum_{i=0}^{N-1} \sum_{j=0}^{M-1} (img(i, j) - \mu)^2

ただし、 img(i, j) は画像の位置 (i, j) の画素値を表す。
分散の平方根 \sigma, (\sigma \le 0)標準偏差という。

関数

meanStdDev()

void cv::meanStdDev(InputArray src,
    OutputArray mean,
    OutputArray stddev,
    InputArray mask = noArray())
mean, stddev = cv2.meanStdDev(src[, mean[, stddev[, mask]]])

コード

#include <iostream>

#include <opencv2/opencv.hpp>

int main()
{
    // 画像を読み込む。
    cv::Mat img = cv::imread("sample.jpg", cv::IMREAD_GRAYSCALE);

    // 画像の平均、分散、標準偏差を計算する。
    cv::Scalar mean, stddev;
    cv::meanStdDev(img, mean, stddev);
    std::cout << "mean: " << mean[0] << std::endl;              // mean: 81.1243
    std::cout << "var: " << stddev[0] * stddev[0] << std::endl; // var: 2251.7
    std::cout << "stddev: " << stddev[0] << std::endl;          // stddev: 47.4521
}
import cv2
import numpy as np

# 画像を読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)

# OpenCV
mean, stddev = cv2.meanStdDev(img)
print(mean, stddev)  # [[81.12428916]] [[47.45210227]]

# numpy
var = np.var(img)
print("var", var)  # var 2251.7020100368704

std = np.std(img)
print("std", std)  # std 47.452102272047654

参考

  • ディジタル画像処理 P60