Pythonの醍醐味といえば、OpenCVなどの強力なライブラリーが使えることだ。
過去に自力でかなりひーこら言いながら実装したフーリエ変換(FFT)や射影変換があっという間にできてしまった。
OpenCVは中間データフォーマットとしてndarrayを使うので、ndarrayで直接操作もできる。
まず普通に開く。
arrayImageColor = cv2.imread(filename, cv2.IMREAD_COLOR) ## cv2 returns ndarray of BGR orientation
arrayImageColorは(512,512,3)のndarrayになる。
これをグレースケールに変換。
arrayImageGray = cv2.cvtColor(arrayImageColor, cv2.COLOR_BGR2GRAY)
arrayImageGrayは(512,512)のシングルチャネルのndarrayになる。
カラーの方を射影変換。
offsetPerspective = 100
(heightImageColor, widthImageColor) = arrayImageColor.shape[:2]
arrayRectangleSource = np.array([[0, 0], [0, heightImageColor], [widthImageColor, heightImageColor], [widthImageColor, 0]], dtype=np.float32)
arrayRectangleTarget = np.array([[offsetPerspective, offsetPerspective], [0, heightImageColor - offsetPerspective], [widthImageColor, heightImageColor - offsetPerspective], [widthImageColor - offsetPerspective, offsetPerspective]], dtype=np.float32)
arrayMatrixPerspective = cv2.getPerspectiveTransform(arrayRectangleSource, arrayRectangleTarget)
arrrayImagePerspective = cv2.warpPerspective(arrayImageColor, arrayMatrixPerspective, (widthImageColor, heightImageColor))
グレーの方はFFT。
arrayFFTed = np.fft.fft2(arrayImageGray)
arrayShiftedFFTed = np.fft.fftshift(arrayFFTed)
arrayPowerSpectrumFFTRealPart = 20 * np.log(np.absolute(arrayShiftedFFTed))
arrayRevertedShiftedFFTed = np.fft.fftshift(arrayShiftedFFTed)
arrayInvertFFTed = np.fft.ifft2(arrayRevertedShiftedFFTed).real
arrayBufferUint8 = arrayInvertFFTed.astype(np.uint8)
FFTのパワースペクトラムは、こうすると(512,512)のuint8のグレースケールとして表示可能なndarrayに変換できる。
ここでは最小を0に、最大を255に正規化しているが、クリップするだけでもいける。
arrayBuffer = np.copy(arrayPowerSpectrumFFTRealPart)
min = np.min(arrayBuffer)
arrayBuffer[:,:] -= min
max = np.max(arrayBuffer)
arrayBuffer[:,:] /= max
arrayBuffer[:,:] *= 255
arrayBufferUint8 = arrayBuffer.astype(np.uint8)
ここからがOpenCVの真骨頂、顔と目の認識。
pathCascadeEye = os.path.join(pathCascade, 'haarcascade_eye.xml')
pathCascadeFrontFace = os.path.join(pathCascade,'haarcascade_frontalface_default.xml')
cascadeClassifierFace = cv2.CascadeClassifier(pathCascadeFrontFace) # it instatiates cascade classifier object
cascadeClassifierEye = cv2.CascadeClassifier(pathCascadeEye)
arrayFaces = cascadeClassifierFace.detectMultiScale(arrayImageGray) # it returns the list of ([tartX, startY, width, height]
colorFace = (255, 0, 0) # color is tuple in CV
colorEye = (0, 255, 0)
LineThickness = 2
for (facePositionX, facePositionY, faceWidth, faceHeight) in arrayFaces:
cv2.rectangle(arrayImageColor, (facePositionX, facePositionY), (facePositionX + faceWidth, facePositionY + faceHeight), colorFace, LineThickness)
arrayBufferFace = arrayImageColor[facePositionY: facePositionY + faceHeight, facePositionX: facePositionX + faceWidth]
arrayBufferFaceGray = arrayImageGray[facePositionY: facePositionY + faceHeight, facePositionX: facePositionX + faceWidth]
arrayEyes = cascadeClassifierEye.detectMultiScale(arrayBufferFaceGray)
print(arrayEyes)
for (eyePositionX, eyePositionY, eyeWidth, eyeHeight) in arrayEyes:
cv2.rectangle(arrayBufferFace, (eyePositionX, eyePositionY), (eyePositionX + eyeWidth, eyePositionY + eyeHeight), colorEye, LineThickness)
お次はRGBチャネル別のヒストグラム
[f:id:nobu_macsuzuki:20210101025258p:plain] colors = ('b','g','r') #matlibplot has the color index, https://matplotlib.org/3.1.0/api/colors_api.html#module-matplotlib.colors
for (indexColor, color) in enumerate(colors): # it provides list of object with index, in this case [(0, 'b'), (1, 'g'), (2, 'r')]
arrayHistogram = cv2.calcHist([arrayImageColor],[indexColor],None,[256],[0,256]) # it retunrs list of channel histogram
plt.plot(arrayHistogram, color = color)
plt.xlim([0,256])
自作したときには、FFTとかライブラリだけで200-300行はあったし、画像を開いてFFTに流せるようにバイト列に変換してうんぬんだって書かねばならない。
射影変換も同様で、affine変換(線形変換)は簡単なのだが、射影変換はひーこらひーこらいいながら書いた。
それが上記の通り、どれも数行 - 十数行で書けてしまうのがすごい、うーむ、恐ろしい生産性。
ちなみに画像の表示はこのようにする。
cv2.imshow('Color image',arrayImageColor)
cv2.waitKey(0)
cv2.destroyAllWindows()
一行目が表示、二行目が入力待ちで、キー入力があると3行目でウィンドウを閉じる。
github.com