使用命名(FIFO)管道在Python和C++之间传输数组(图像)

4
我需要将一个数组(表示图像)从Python进程通过命名FIFO管道发送到C ++进程,然后再返回另一侧(在Linux系统上)。

当在两个Python进程之间使用命名管道时,下面的代码效果很好。它使用numpy的tostring()和fromstring()函数:

通过命名管道发送帧(Python)

import cv2
import numpy as np
from time import sleep

##########################################################

FIFO_Images = "./../pipes/images.fifo"
videoName = "./../../videos/videoName.avi"
delim = "break"

##########################################################


def sendImage(h, w, d, pixelarray):
    imageString = pixelarray.tostring()
    with open(FIFO_Images, "w") as f:
        f.write(str(h)+ delim + str(w)+ delim + str(d) + delim + imageString)
    sleep(.01)  
    return 

##########################################################

cap = cv2.VideoCapture(videoName)

while(cap.isOpened()):
    ret, frame_rgb = cap.read() 
    h, w, d = frame_rgb.shape
    sendImage(h, w, d, frame_rgb)

cap.release()
cv2.destroyAllWindows()

使用命名管道读取帧(Python)

import cv2
import numpy as np

##########################################################

FIFO_Images = "./../pipes/images.fifo"
delim = "break"

##########################################################

def getFrame():
    with open(FIFO_Images, "r") as f:
        data = f.read().split(delim)

        #parse incoming string, which has format (height, width, depth, imageData)        
        h=int(data[0])
        w=int(data[1])
        d=int(data[2])
        imageString = data[3]

        #convert array string into numpy array
        array = np.fromstring(imageString, dtype=np.uint8)

        #reshape numpy array into the required dimensions
        frame = array.reshape((h,w,d))

        return frame  

##########################################################

while(True):

    frame = getFrame()

    cv2.imshow('frame', frame)
    cv2.waitKey(1) & 0xFF 

然而,在cpp侧面从管道中读取整个图像是我无法解决的问题,因为它自动将“\n”作为读取的分隔符。我的解决方法是对“tostring()”图像进行base64编码,然后将其发送到管道上。这种方法可以实现,但在实时应用中,另一端的base64解码速度太慢(每帧大约0.2秒)。代码如下: 在命名管道上发送base64编码图像(Python)
import cv2
import numpy as np
from time import time
from time import sleep
import base64

##########################################################

FIFO_Images = "./../pipes/images.fifo"
videoName = "./../../videos/videoName.avi"

delim = ";;"

##########################################################

def sendImage(h, w, d, pixelarray):


    flat = pixelarray.flatten()

    imageString = base64.b64encode(pixelarray.tostring())
    fullString = str(h)+ delim + str(w)+ delim + str(d)+ delim + imageString + delim + "\n"


    with open(FIFO_Images, "w") as f:
        f.write(fullString)

    return 

##########################################################

cap = cv2.VideoCapture(videoName)
count = 0

while(cap.isOpened()):
    ret, frame_rgb = cap.read()
    h, w, d = frame_rgb.shape

    frame_gbr = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR)

    sendImage(h, w, d, frame_rgb)


cap.release()
cv2.destroyAllWindows()

使用命名管道(C++)读取base64编码的图像

#include "opencv2/opencv.hpp"

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <unistd.h>
#include <fcntl.h>

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <linux/stat.h>
#include <ctime>

using namespace std;
using namespace cv;

#define FIFO_FILE "./../../../pipes/images.fifo"
#define MAX_BUF 10000000

FILE *fp;
char readbuf[MAX_BUF + 1];  //add 1 to the expected size to accomodate the mysterious "extra byte", which I think signals the end of the line. 


/************************BASE64 Decoding*********************************************/
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";


static inline bool is_base64(unsigned char c) {
  return (isalnum(c) || (c == '+') || (c == '/'));
}

std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
  std::string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--) {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';

  }

  return ret;

}

std::string base64_decode(std::string const& encoded_string) {
  int in_len = encoded_string.size();
  int i = 0;
  int j = 0;
  int in_ = 0;
  unsigned char char_array_4[4], char_array_3[3];
  std::string ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
        ret += char_array_3[i];
      i = 0;
    }
  }

  if (i) {
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
  }

  return ret;
}

/*********************************************************************/

int stringToInt(string str)
{
    int num;
    if (!(istringstream(str) >> num)) num = 0;

    return num;
}

/*********************************************************************/
bool timerOn = 0;
clock_t timerStart;

void Timer(string process)
{

    if (!timerOn)
    {
        timerStart = clock();
        timerOn = true;
    }

    else if (timerOn)
    {
        double duration = (clock() - timerStart) / (double) CLOCKS_PER_SEC; 
        cout << "Time to complete: ";
        printf("%.2f", duration);
        cout << ": " << process << endl;
        timerOn = false;
    }
}

/*********************************************************************/

void getFrame()
{
    string fullString;
    string delimiter = ";;";
    size_t pos = 0;
    string token;

    int h;
    int w;
    int d;
    string imgString;

    int fifo;
    bool cont(true);

    /***************************
    Read from the pipe
    www.tldp.org/LDP/lpg/node18.html
    ***************************/
    Timer("Read from pipe");
    fp = fopen(FIFO_FILE, "r");
    fgets(readbuf, MAX_BUF + 1, fp); // Stops when MAX_BUF characters are read, the newline character ("\n") is read, or the EOF (end of file) is reached
    string line(readbuf);
    fclose(fp);
    Timer("Read from pipe");

    //////parse the string into components

    Timer("Parse string");
    int counter = 0;
    while ((pos = line.find(delimiter)) != string::npos)
    {
        token = line.substr(0,pos);

        if (counter == 0)
        {
            h = stringToInt(token);
        }
        else if (counter == 1)
        {
            w = stringToInt(token);
        }
        else if (counter == 2)
        {
            d = stringToInt(token);
        }
        else if (counter == 3)
        {
            imgString = token;
            //cout << imgString[0] << endl;
        }
        else
        {
            cout << "ERROR: Too many paramaters passed" << endl;
            return;
        }

        line.erase(0, pos + delimiter.length());

        counter ++;
    }

    if (counter == 3)
    {
        imgString = token;
    }

    if (counter < 3)
    {
        cout << "ERROR: Not enough paramaters passed: " << counter << endl;
        //return;
    }

    Timer("Parse string");



    /***************************
    Convert from Base64
    ***************************/      
    Timer("Decode Base64");
    std::string decoded = base64_decode(imgString);
    Timer("Decode Base64");

    /***************************
    Convert to vector of ints
    ***************************/
    Timer("Convert to vector of ints");   
    std::vector<uchar> imgVector;   
    for (int i = 0; i < decoded.length(); i = i+1) // + 4)
    {
        int temp =  (char(decoded[i]));
        imgVector.push_back(temp);
    }
    Timer("Convert to vector of ints");

    //////convert the vector into a matrix
    Mat frame = Mat(imgVector).reshape(d, h);   

    namedWindow("Frame", WINDOW_AUTOSIZE);
    imshow("Frame", frame);

    waitKey(1); 
}


int main()
{

    /* Create the FIFO if it does not exist */
    umask(0);
    mknod(FIFO_FILE, S_IFIFO|0666, 0);

    while(1)
    {
        getFrame();

    }

    return 0;
}

必须有更有效的方法来完成这个任务。有人可以给出建议吗?虽然我很乐意听取其他完成此任务的方法的建议,但现在我只能使用命名管道

1个回答

5
这太过复杂了。如果你需要发送二进制数据,先发送其长度,然后是换行符 (\n),再接着发送数据(原始数据,不用base64编码)。在另一端接收时,先读取一行,解析出数字,然后只需读取给定长度的数据块即可。
示例 - 在 Python 中将二进制数据写入到 FIFO(或文件)中:
#!/usr/bin/env python3

import os

fifo_name = 'fifo'

def main():
    data = b'blob\n\x00 123'
    try:
        os.mkfifo(fifo_name)
    except FileExistsError:
        pass
    with open(fifo_name, 'wb') as f:
        # b for binary mode
        f.write('{}\n'.format(len(data)).encode())
        f.write(data)

if __name__ == '__main__':
    main()

使用C++从FIFO读取二进制数据:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include <sys/stat.h>

int main(int argc, char *argv[]) {
    const char *fifo_name = "fifo";
    mknod(fifo_name, S_IFIFO | 0666, 0);
    std::ifstream f(fifo_name);
    std::string line;
    getline(f, line);
    auto data_size = std::stoi(line);
    std::cout << "Size: " << data_size << std::endl;
    std::string data;
    {
        std::vector<char> buf(data_size);
        f.read(buf.data(), data_size);
        // write to vector data is valid since C++11
        data.assign(buf.data(), buf.size());
    }
    if (!f.good()) {
        std::cerr << "Read failed" << std::endl;
    }
    std::cout << "Data size: " << data.size() << " content: " << data << std::endl;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接