1. 程式人生 > 實用技巧 >python實現簡單的視訊傳輸與處理

python實現簡單的視訊傳輸與處理

1. opencv從攝像頭抽幀
camera = cv2.VideoCapture(0)
if camera.isOpened():
     success, frame = camera.read()
          if success:
               print('capture success')
2. RGB轉YUV編碼,JPG格式壓縮
# 直接壓縮到最小
result, img_code = cv2.imencode('.jpg', frame)

# 可以指定壓縮後的影象質量
img_quality = 15
result, img_code = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), img_quality])

# 這裡,img_code經過編碼後,可以直接tobytes寫入檔案了。
3. numpy ndarray 轉bytes
buffer = frame.tobytes()
4. numpy ndarray 從buffer讀取影象矩陣
# buffer中讀取矩陣需要手動指定dtype,之後reshape調整shape,因此如果通過網路傳輸矩陣,需要同時傳輸其dtype和shape。

buffer = numpy.frombuffer(frame.tobytes(), frame.dtype)
buffer.reshape(frame.shape)
5. 影象轉為矩陣
frame = cv2.imdecode(numpy.frombuffer(img_code.tobytes(), img_code.dtype), -1)
img = Image.fromarray(frame)

# 一般來說,影象矩陣元素型別為 uint8 , 解碼時可直接指定dtype為 numpy.uint8
6. BGR 轉 RGB 的幾種方式
# 如果發現圖片顯示的時候顏色不對勁,紅色變成了藍色,說明顏色資訊放反了,需要轉換一下
# opencv預設使用BGR格式儲存影象

frame = frame[:, :, [2, 1, 0]]
frame = frame[:, :, ::-1]
frame = frame[..., ::-1]
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
7. opencv 、socket 實現簡單的視訊處理(不帶音訊)

視訊源 —> 生產者 —> 中間處理 —> 消費者

視訊源:可以是靜態視訊檔案,也可以是一些實時視訊流。

生產者:opencv的VideoCapture,從視訊源抽幀。

中間處理:影象處理程式等。

消費者:播放器、視訊封裝程式等。

server.py
class FrameProducer(threading.Thread):

    def __init__(self, frame_stack):
        super(FrameProducer, self).__init__()
        self.camera = cv2.VideoCapture(0)
        self.running = True
        self.frame_stack: Stack = frame_stack

    def run(self) -> None:
        while self.running:
            if self.camera.isOpened():
                res, frame = self.camera.read()
                if res:
                    print('clip a frame from camera')
                    self.frame_stack.push(frame)

class Sender(threading.Thread):
    def __init__(self, sock: socket.socket, frame_stack: Stack):
        super(Sender, self).__init__()
        self.client: socket = sock
        self.resolution = (640, 480)
        self.img_quality = 15
        self.running = True
        self.frame_stack = frame_stack

    def run(self) -> None:
        while self.running:
            frame = self.frame_stack.pop()
            if frame is not None:
                print('got a frame from stack')

                frame = cv2.resize(frame, self.resolution)
                result, img_encode = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), self.img_quality])
                data = img_encode.tostring()
                msg = Message(data, *self.resolution)

                try:
                    self.client.send(msg.get_head())
                    self.client.send(msg.get_body())

                    print(msg.length)
                except Exception as ex:
                    self.running = False
                    print(ex)
client.py
class Receiver(threading.Thread):
    def __init__(self, _sock: socket.socket, frame_stack: Stack):
        super(Receiver, self).__init__()
        self.sock = _sock
        self.running = True
        self.frame_stack = frame_stack

    def run(self) -> None:
        try:
            while self.running:
                head = self.sock.recv(Message.head_length)
                msg = Message()
                msg.parse_head(head)

                data = self.sock.recv(msg.length)
                msg.parse_body(data)
                print(msg.length, len(msg.data))
                frame = cv2.imdecode(np.frombuffer(msg.data, np.uint8), -1)
                self.frame_stack.push(frame)

        except Exception as ex:
            print(ex)
            self.running = False


class Consumer(threading.Thread):
    def __init__(self, frame_stack: Stack):
        super(Consumer, self).__init__()
        self.frame_stack = frame_stack
        self.running = True

    def run(self) -> None:
        while self.running:
            frame = self.frame_stack.pop()
            if frame is not None:
                cv2.imshow('image', frame)
                if cv2.waitKey(100) & 0xFF == ord('q'):
                    break
可能遇到的問題:
  1. 如果消費者這邊處理速度低於opencv抽幀的速度,由於opencv自帶幀緩衝區,每一幀影象都不會被丟棄,會使幀資料在緩衝區堆積,結果處理後的視訊延時越來越高。消費者端,準備一個棧,將接收到的幀存放到棧裡,用於丟幀,防止實時視訊的延時累加。每當棧內積壓的資料超過一個閥值,就將棧內資料清空,防止記憶體溢位。
  2. 直接使用socket時需要注意socket的粘包問題。粘包問題可以通過多種方式解決,如定界符加轉義、固定報文長度、固定首部長度並在首部指明資料部分長度。