画像の表示
画面を作っておく
トラックバーの作成や,画面サイズの調整に必要.
cv2.namedWindow('name', flag)
flag:
ウィンドウサイズを調整できるか
画像の比率を固定するか
拡張GUIを使用するか
を指定できる
画面サイズを調整する
cv.resizeWindow(winname, size) -> None
ウィンドウに,cv2.WINDOW_AUTOSIZEフラグが付与されているとだめ.
cv2.imshowで表示したウィンドウは cv2.WINDOW_AUTOSIZEフラグが付与される.
このフラグを付与しないようにするには,cv2.namedWindow('', cv2.WINDOW_NORMAL)を呼ぶ必要がある.
画面の位置調整
cv.moveWindow(winname, x, y) -> None
winname:
Name of the window.
x, y:
The new x-coordinate of the window.
画像のチャンネルを分割する
img_h, img_s, img_v = cv2.split(img)
画像のチャンネルを結合する
cv.merge(mv[, dst]) -> dst
mv:
入力行列
全て同じサイズ,同じビット深度出ないとダメ
BGRの順.HSVの場合は,その順で,cv2COLORする
HSV画像にする
img_hsv = cv2.cvtColor(self.img, cv2.COLOR_BGR2HSV)
img_hsv = cv2.cvtColor(self.img, cv2.COLOR_BGR2HSV_FULL)
cv2.COLOR_BGR2HSVは0以上180以下(未満?)の値,
cv2.COLOR_BGRE2HSV_FULLは0以上255以下の値.
注意:
_FULLの方だと,mergeすると色がおかしくなる.(Hの値がおかしくなるため)
ヒストグラム
ヒストグラムの表示
1ch画像のヒストグラムをmatplotlibで出力する
from matplotlib import pyplot as plt
img = cv2.imread('wiki.jpg',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/ cdf.max()
plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
plt.show()
適用的ヒストグラム平坦化
画像を全体的に平坦化(cv2.equalizeHist)するとあんまりうまくいかないので「適用的ヒストグラム平坦化」がおすすめ.次のようにやる.
clahe = cv2.createCLAHE(clipLimit, tileGridSize)
cl1 = clahe.apply(img)
cv.createCLAHE([, clipLimit[, tileGridSize]]) ->retval
CLAHEクラスのコンストラクタ
clipLimit=40.0: double
コントラストの制限(?)
tileGridSize=(8, 8): Size(←Cの場合,pyの場合はtupleでよさそう)
グリッドサイズ.画像は均等な大きさの矩形に分割される.
cv.CLAHE.apply(src[, dst]) -> dst
src:
CV_8UC1 or CV_16UC1タイプの1chの画像
dst:
目的の画像.
行列関係の操作
https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#ga0547c7fed86152d7e9d0096029c8518a
複数チャンネルから1chを取り出す
img_r = img[:, :, 0]
img_g = img[:, :, 1]
img_b = img[:, :, 2]
img_r, img_g, img_b = cv.split(img)
複数の1chでマルチチャンネルを作る
cv.merge
cv.splitの逆.
さらに複雑なものをつく流にはcv.mixCnannel.
ビット演算4
AND
cv.bitwise_and(src1, src2[, dst[, mask]]) -> dst
src1, src2:
入力画像.共に配列か,配列とスカラ.
dst(i) = src1(i) ∧ src2(i) if mask(i) ≠ 0.
(スカラの場合はちょっとわからん.)
src1とsrc2が共に画像で,異なるチャンネルだと次のエラーが出るので注意.
214: error: (-209:Sizes of input arguments do not match) The operation is neither 'array op array' (where arrays have the same size and type), nor 'array op scalar', nor 'scalar op array' in function 'binary_op'
もし,チャンネルが違う場合,maskに1ch使う.
cv.bitwise(src1_3ch, src1_3ch, mask=src2_1ch)
??3chの場合,それぞれのチャンネルがどう作用するのか.
NOT
OR
XOR
dst:
mask:
重ねる
cv.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) -> dst
src1, src2:
3ch画像ok.
dst(i) = src1 * alpha + src2 * beta + gamma
オーバーフローしないように.
画像作る
dtype=np.uint8
画像の差分
cv.absdiff(src1, src2[, dst]) -> dst
src1, src2が同じサイズ,同じ型の配列の場合,
src1, src2のいずれかが,スカラ(の配列?)か,src1と同じチャンネル数の場合,
微分 (Image Filtering)
Sobelフィルター
ガウス平滑化と微分化を組み合わせたようなやつ.
他の1次微分フィルタと比べるとノイズに強い.
次のカーネルでフィルターで畳み込む.
-1 0 1
-2 0 2
-1 0 1
ラプラシアンフィルター(2次微分)と比べると,ボケててもエッジが検出できる.
cv.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]]) -> dst
sec:
入力画像
ddepth: int
出力画像の深度.
cv2.CV_64Fn
dx, dy: int
x, yの次数(?).
大体dx=1, dx=0 or dx=0, dx=1.
ksize=3: int
1, 3, 5, 7から選択.
ksize=1の場合は,3x1,1x3のカーネルが使用される.これは,ガウス平滑化は行われないことを意味する.
1次か2次の時しか使えない(?).
それ以外では,ksize x ksizeのカーネルが使用される.
また,3x3Sobelよりも正確(?)な値が取れる3x3Scharrフィルタも指定できる
sksize=cv2.FILTER_SCHARR(-1)
Shareフィルタは,カーネル.
-3 0 3
-10 0 10
-3 0 3
scale=1: double
デフォルトではスケーリングは行われない.
delta=0: double
結果の各値に追加される値.
borderType=BORDER_DEFALUT: borderType
周りのところの埋め方.
BORDER_WRAPはサポートされず.
エッジ検出(Feature Detection)
Canny法
ガウシアンフィルタで平滑化後、閾値1(threshold1)以上の濃度変化の大きいところをエッジとみなし、閾値2(threshold2)以上でかつ,閾値1でエッジとみなした付近のところをエッジとみなす.
cv.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]]) -> edges
cv.Canny(dx, dy, threshold1, threshold2[, edges[, L2gradient]]) -> edges
image:
8-bit入力画像.
edges:
8-bit出力画像.入力画像と同サイズ.
threshold1,2: double
ヒステリシス閾値.
apertureSize=3: int
Sobleフィルターのサイズ.(1, 3, 5, 7?)
L2gradient=false: bool
画像の勾配の大きさを計算するのに使用するノルムをより正確なものに使用するか.
トラックバー(High-level GUI)
作る
すでにあるウィンドウにトラックバーを作成する.
cv.createTrackbar(trackbarname, winname, value, count, onChange, userdata) -> int
※ 公式ドキュメントには,C言語用のコードのみの記載.pythonコードの表記方法がなかったので,不適切な説明の場合がある.
trackbarname: str
トラックバーの名称
winnname: str
トラックバーをつけるウィンドウ名.
先にウィンドウを作成しないとだめ.
value: int
トラックバーのスライダーの初期値.
count: int
スライダーのスライダーの最大値.
なお,最小値は常に0.
onChange=0: TrackbarCallback(pos: int, *userdata: void)
※ pythonの場合必須そう.
トラックバーのスライダー位置が変わるたびに呼び出されるコールバック関数.
関数の引数 pos: トラックバーのスライダーの位置.
関数の引数 userdata: 次の項目を参照.
userdata=0:
コールバック関数にそのまま渡される.
グローバル変数を使用しないでトラックバーイベントを処理するために使用可能.
※ pythonの場合,このままできない可能性あり.
※ クラスを使用すると可能かも:https://stackoverflow.com/questions/40680795/cv2-createtrackbar-pass-userdata-parameter-into-callback
1.トラックバー作る
2.マスク画像作成
3.色抽出
4.表示,待機
5.入力されたら次,トラックバー動いたら次
トラックバーの位置を取得する
指定したトラックバーのスライダー位置を取得できる.
cv.getTrackbarPos(trackbarname, winname) ->retval
trackbaarname:
トラックバーの名前
winname:
ウィンドウの名前.
トラックバーの位置を与える
cv.setTrackbarPos(trackbarname, winname, pos) -> None
トラックバーの最小値,最大値を指定する
cv.setTrackbarMin()
cv.setTrackbarMax()
背景削除する
Kmeans法によるクラスタリング(Clustering)
cv.kmeans(data, K, bestLabels, criteria, attempts, flags[, centers]) -> retval, bestLabels, centers
指定した数のクラスに分類する
data:
入力データ.float座標N次元データ.
data = img.reshape(-1, 3).astype(np.float32)
K: int
分割するクラスタ数.
bestLabels: InputOutputArray
クラスタのインデックスを格納する整数型配列.
渡さないならNoneでいいです.
大体のラベリングを指定したいときに使う.ラベル値が結局ランダム?
criteria: TermCriteria
終了基準(最大反復回数or精度or両者).
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 10, 1.0)
attempts: int
試行回数.
flags: int
次のcv2.KmeansFlagの値のいずれか.
ランダム:cv2.KMEANS_RANDOM_CENTERS
Arthur, Vassilvitskii [Arthur2007]によるkmeans++の初期値(?):cv2.KMEANS_PP_CENTERS
指定したラベルを使用した値(?):cv2.KMEANS_USE_INITIAL_LABELS
centers: OutputArray
クラスタの中心行列.各クラスタ中心で1行ごとの値.
基底0.