OpenCV3計算機視覺:Python實現 讀書筆記-第二章

第二章

使用numpy.array訪問圖像數據

改變一個特定像素的值:

  • numpy.array提供的item()
    • item(x,y,id)
    • id爲索引,B G R
  • itemset()
    • itemset((x,y,id),val)
    • (x,y,索引,要設定的值)

操作通道:將指定通道所有值置0

import cv2
import numpy as np
img = cv2.imread('1.png')
img[:,:,1] = 0

視頻文件的讀/寫

  • OpenCV提供了VideoCapture類和VideoWriter類支持各種格式的視頻文件(都支持AVI格式),到達視頻文件末尾前,可通過read()獲取新的幀,每一幀是一幅基於BGR格式的圖像
import cv2
videoCapture = cv2.VideoCapture('2_1.avi')
fps = videoCapture.get(cv2.CAP_PROP_FPS)#幀速率
size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
        int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
videoWriter = cv2.VideoWriter('2_1_copy.avi',cv2.VideoWriter_fourcc('I','4','2','0'),fps,size)

sucess , frame = videoCapture.read()
while sucess:
    videoWriter.write(frame)
    sucess , frame = videoCapture.read()

其中編解碼器的可用性常用選項:

  • cv2.VideoWriter_fource('I','4','2','0'):該選項是一個未壓縮的YUV顏色編碼,4:2:0色度子採樣。這種編碼有很好地兼容性,但會產生較大文件,文件擴展名.avi (例子視頻145K,產生了12.4M文件)
  • cv2.VideoWriter_fource(‘P’,‘I’,‘M’,‘1’): 生成.avi
  • cv2.VideoWriter_fource(‘X’,‘V’,‘I’,‘D’):MPEG-4編碼類型,生成.avi(若希望視頻大小爲平均值,推薦使用)
  • cv2.VideoWriter_fource(‘F’,‘L’,‘V’,‘1’):該選項是一個Flash視頻,擴展名.flv

捕獲攝像頭的幀

import cv2

cameraCapture = cv2.VideoCapture(0)
#爲了針對攝像頭創建合適的VideoWriter類,
#要麼對幀速率作出假設,
#要麼使用計時器測量
fps = 30
size = (int(cameraCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
        int(cameraCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))

videoWriter = cv2.VideoWriter('2_1_catch_camera.avi',cv2.VideoWriter_fourcc('I','4','2','0'),fps,size)

success , frame = cameraCapture.read()
numFramesRemaining = 10 * fps - 1

while success and numFramesRemaining > 0 :
    videoWriter.write(frame)
#需要同步一組攝像頭,read()方法不適用,可用grab(),retrive()替代
    success , frame = cameraCapture.read()
    numFramesRemaining -= 1

cameraCapture.release()

imshow在窗口顯示圖像

import cv2
import  numpy as np
img = cv2.imread('2_1.jpg')
cv2.imshow('2_1.jpg',img)
cv2.waitKey()#保證顯示視頻時窗口上幀可以一直更新
cv2.destroyAllWindows()

在窗口顯示攝像頭幀

  • 任意窗口下都可以通過waitKey()獲取鍵盤輸入;
  • 通過setMouseCallback()獲取鼠標輸入。
#實時顯示攝像頭幀

#OpenCV不提供任何處理窗口事件的方法,單擊窗口關閉時,並不能關閉應用程序
import cv2
clicked = False
#鼠標響應函數
"""
flags: 代表鼠標的拖拽事件,以及鍵盤鼠標聯合事件
param:函數指針 標識了所響應的事件函數,相當於自定義了一個OnMouseAction()函數的ID。
"""
def onMouse( event , x , y , flags , param ):
    global clicked
    if event == cv2.EVENT_LBUTTONUP:
        clicked = True


cameraCapture = cv2.VideoCapture(0)
cv2.namedWindow('MyWindow')

#鼠標回調函數,param爲可選參數
"""
C++中原型:
void setMouseCallback(const string& winname,     //圖像視窗名稱
MouseCallback onMouse,     //鼠標響應函數,監視到鼠標操作後調用並處理相應動作
void* userdata = 0        //鼠標響應處理函數的ID,識別號,默認0
);
"""
cv2.setMouseCallback('MyWindow',onMouse)

print('Showing camera feed. Click window or press any key to stop.')

sucess , frame = cameraCapture.read()

#waitKey()參數爲等待鍵盤觸發的時間,單位ms,返回值-1(表示沒有被按下)
#某些系統waitKey()返回值可能比ASCII更大,可通過讀取返回值最後一個字節
#保證只提取ASCII碼:
# keycode = cv2.waitKey(1)
# if keycode != -1 :
#     keycode &= 0xFF

while sucess and cv2.waitKey(1) == -1 and not clicked:
    cv2.imshow('MyWindow',frame)
    sucess , frame = cameraCapture.read()

cv2.destroyWindow('MyWindow')
cameraCapture.release()

Cameo-面向對象設計

  • 使用多個I/O流
  • 創建CaptureManager類和WindowManager類作爲高級的I/O流接口

使用managers.CaptureManager提取視頻流

使用windowManager抽象窗口和鍵盤

監聽鍵盤和鼠標事件:進行截圖,截屏

運行時,攝像頭幀被鏡像,存儲圖片也被鏡像(默認設置鏡像爲True)
managers.py

#managers.CaptureManager提取視頻流
import cv2
import numpy
import time

class CaptureManager(object):

    def __init__(self,capture,previewWindowManager = None,shouldMirrorPreview = False):

        self.previewWindowManager = previewWindowManager
        self.shouldMirrorPreview = shouldMirrorPreview

        self._capture = capture
        self._channel = 0
        self._enteredFrame = False
        self._frame = None
        self._mirroredframe = None
        self._imageFilename = None
        self._videoFilename = None
        self._videoEncoding = None
        self._videoWriter = None

        self._startTime = None
        self._framesElapsed = numpy.long(0)
        self._fpsEstimate = None

    @property
    def channel(self):
        return self._channel

    @channel.setter
    def channel(self,value):
        if self._channel != value:
            self._channel = value
            self._frame = None

    @property
    def frame(self):
        if self._enteredFrame and self._frame is None:
            _ , self._frame = self._capture.retrieve() #retrieve()是解碼並返回一個幀,grab是指向下一個幀(不需要當前幀時可跳過),read是grab和retrieve的結合
        return self._frame

    @property
    def isWritingImage(self):
        return self._imageFilename is not None

    @property
    def isWritingVideo(self):
        return  self._videoFilename is not None

    def enterFrame(self): #Capture the next frame
        #check previous frame was existed.
        # 檢查上一幀是否退出
        assert not self._enteredFrame , \
        'previous enterFrame() had no matching exitFrame()'

        if self._capture is not None :
            self._enteredFrame = self._capture.grab() #用grab()指向下一幀
    def exitFrame(self):
        #draw to the window ,write to files , release the frame.
        if self.frame is None:
            self._enteredFrame = False
            return

        if self._framesElapsed == 0 :
            self._startTime = time.time()
        else:
            timeElapsed = time.time() - self._startTime
            self._fpsEstimate = self._framesElapsed / timeElapsed
            self._framesElapsed += 1
        #draw to the window
        if self.previewWindowManager is not None:
            if self.shouldMirrorPreview:
                self._mirroredframe = numpy.fliplr(self._frame).copy()
                self.previewWindowManager.show(self._mirroredframe)
            else:
                self.previewWindowManager.show(self._frame)

        if self.isWritingImage:
            if self.shouldMirrorPreview:
                cv2.imwrite(self._imageFilename,self._mirroredframe)
            else :
                cv2.imwrite(self._imageFilename, self._frame)
            self._imageFilename = None

        self._writeVideoFrame()
        self._frame = None
        self._enteredFrame = False

    def writeImage(self , filename):
        self._imageFilename = filename
    def startWritingVideo(self,filename,encoding=cv2.VideoWriter_fourcc('I','4','2','0')):
        self._videoFilename = filename
        self._videoEncoding = encoding
    def stopWritingVideo(self):
        self._videoFilename = None
        self._videoEncoding = None
        self._videoWriter = None
    def _writeVideoFrame(self): #非公有
        if not self.isWritingVideo:
            return
        if self._videoWriter is None:
            fps = self._capture.get(cv2.CAP_PROP_FPS)
            if fps == 0.0:
                if self._framesElapsed < 20 :
                    return
                else:
                    fps = self._fpsEstimate
            size = (int(self._capture.get(cv2.CAP_PROP_FRAME_WIDTH)),int(self._capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
            self._videoWriter = cv2.VideoWriter(self._videoFilename,self._videoEncoding,fps,size)
        self._videoWriter.write(self._frame)



#抽象窗口和鍵盤
class WindowManager(object):
    def __init__(self,windowName,keypressCallback = None,mousepressCallback = None):
        self.keypressCallback = keypressCallback
        self.mousepressCallback = mousepressCallback #鼠標
        self._windowName = windowName
        self._isWindowCreated = False

    @property
    def isWindowCreated(self):
        return self._isWindowCreated

    def createWindow(self):
        cv2.namedWindow(self._windowName)
        self._isWindowCreated = True
        cv2.setMouseCallback(self._windowName,self.mousepressCallback) #鼠標事件

    def show(self , frame):
        cv2.imshow(self._windowName,frame)

    def destoryWindow(self):
        cv2.destroyWindow(self._windowName)
        self._isWindowCreated = False

    def processEvent(self):
        keycode = cv2.waitKey(1)
        if self.keypressCallback is not None and keycode != -1:
            keycode &= 0xFF
            self.keypressCallback(keycode)


#python內置的@property裝飾器就是負責把一個方法變成屬性調用的

cameo.py

import cv2
from managers import WindowManager , CaptureManager

class Cameo(object):
    def __init__(self):
        self._windowManager = WindowManager('cameo',self.onKeypress,self.onMouse)
        self._captureManager = CaptureManager(cv2.VideoCapture(0),self._windowManager,True)

    def run(self):
        self._windowManager.createWindow() #創建窗口

        while self._windowManager.isWindowCreated:
            self._captureManager.enterFrame()     #檢查上一幀是否退出,並指向下一幀
            frame = self._captureManager.frame    #獲取當前幀

            self._captureManager.exitFrame()      #draw to the window ,write to files , release the frame.
            self._windowManager.processEvent()    #處理鍵盤事件


    def onKeypress(self , keycode): #鍵盤響應
        """
        Handle a keypress.
            space  -> Take a screenshot
            tab    -> Start/Stop recording a screencast
            escape -> Quit
        """
        if keycode == 32: #space
            self._captureManager.writeImage('screenshot.png')
        elif keycode == 9:#tab
            if not self._captureManager.isWritingVideo:
                self._captureManager.startWritingVideo('screencast.avi')
            else:
                self._captureManager.stopWritingVideo()
        elif keycode == 27:#escape
            self._windowManager.destoryWindow()

    def onMouse(self , event, x, y, flags, param): #鼠標響應函數
        if event == cv2.EVENT_LBUTTONUP:
            self._windowManager.destoryWindow()
if __name__=="__main__":
    Cameo().run()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章