buffered read/write of image data between parent and child is not completing

喜你入骨 提交于 2019-12-11 19:45:15

问题


Introduction and general objective

This question originated from this one where I was trying to send image data from the child to the parent. The problem in that case was using buffered and unbuffered read functions. You can find working code of the old question at the bottom of this one.

Now I am trying to send an image from a parent process to the child process (generated by calling popen from the parent) that is supposed to show the iamge.

The image is a grayscale png image. It is opened with the OpenCV library and encoded using imencode function of the same library. So the resulting encoded data is stored into a std::vector structure of type uchar, namely the img vector in the code below.

No error in sending some data

The size of the image is hard coded in child in the variable img_size for simplicity). This is used to allocate the memory where received data is stored. The allocation is the following:

    u_char *buf = (u_char*)malloc(img_size*sizeof(u_char));

Sending image data

The parent writes the encoded data (i.e. the data contained in the vector img) to the FILE* stream returned by popen using fwrite while the child reads the data with fread using as FILE* the result of the conversion of the STDIN_FILENO file descriptor:

FILE * fp = fdopen(STDIN_FILENO, "r");

Data writing is performed in blocks 4096 bytes inside while loop while data reading reads in two calls of fread (I know it's ugly but this will be changed).

The writing loop is the following:

while (written<img.size())
{
    //bytes to send: 4096 or the remaining ones if less than 4096
    toWrite = BUFLEN < (img.size()-written) ? BUFLEN : (img.size()-written);
    //total bytes that have been sent until now
    written += toWrite*fwrite ( img.data()+written, toWrite, 1, f );
    printf("written: %ld\n", written);
 }

img.data() returns the pointer to the first element in the array used internally by the vector structure. written stores the number of bytes that have been written until now and it is used as index. fwrite returns the number of blocks (of size toWrite bytes each) that actually have been written and this is used to update written.

Data reading is very similar and it is performed by the following line (bytes2Copy is initialized to 4096):

//read all possible bytes in blocks of 4096 until less than 4096 bytes remain
elRead = fread ( buf, bytes2Copy, img_size/bytes2Copy, fp);
total_bytes_read += elRead*bytes2Copy;
bytes2Copy = img_size-total_bytes_read; //now less than 4096 bytes remain
printf("child received %ld\n", total_bytes_read);
//read remaining bytes
elRead = fread ( buf+total_bytes_read, bytes2Copy, 1, fp);

buf is the array where received data is stored. bytes2Copy is either BUFLEN (i.e. 4096) or for the last block of data the remaining data (if for example the total bytes are 5000 then after 1 block of 4096 bytes another block of 5000-4096 is expected).

The code

Consider this example. The following is a process launching a child process with popen

#include <stdlib.h>
#include <unistd.h>//read
#include "opencv2/opencv.hpp"
#include <iostream>
#include <sys/wait.h>
#include "opencv2/opencv.hpp"
#define BUFLEN 4096

using namespace cv;

int main(int argc, char *argv[])
{
    int status;
    Mat frame;
    std::vector<uchar> img;
    //read image as grayscale
    frame = imread("/home/path/img/test.png",0);
    //encode image and put data into the vector buf
    imencode(".png",frame, img);
    //send the total size of vector to parent

    size_t toWrite = 0;
    size_t written= 0;
    FILE * f = popen("/home/path/childProcess", "w");
    printf("forked\n");
    while (written<img.size())
    {
        //send the current block of data
        toWrite = BUFLEN < (img.size()-written) ? BUFLEN : (img.size()-written);
        //written += write(STDOUT_FILENO, buf.data()+written, toWrite);
        written += toWrite*fwrite ( img.data()+written, toWrite, 1, f );
        printf("written: %ld\n", written);
    }
    wait(&status);
    return 0;

}

and the process opened by the above corresponds to the following:

#include <stdlib.h>
#include <unistd.h>
#include "opencv2/opencv.hpp"
#include <iostream>
#include <time.h>
#define BUFLEN 4096

int main(int argc, char *argv[])
{
    cv::Mat frame;
    size_t img_size = 115715;
    u_char *buf = (u_char*)malloc(img_size*sizeof(u_char));
    size_t total_bytes_read = 0;
    size_t elRead =0;
    size_t bytes2Copy = BUFLEN;
    FILE * fp = fdopen(STDIN_FILENO, "r");


    elRead = fread ( buf, bytes2Copy, img_size/bytes2Copy, fp);
    total_bytes_read += elRead*bytes2Copy;
    bytes2Copy = img_size-total_bytes_read;
    printf("child received %ld\n", total_bytes_read);
    elRead = fread ( buf+total_bytes_read, bytes2Copy, 1, fp); //sostituire 1
    total_bytes_read += elRead*bytes2Copy;//bytes_read_tihs_loop;
    printf("child received %ld\n", total_bytes_read);

    cv::namedWindow( "win", cv::WINDOW_AUTOSIZE );
    frame  = cv::imdecode(cv::Mat(1,total_bytes_read,0, buf), 0);
    cv::imshow("win", frame);
    cv::waitKey(0);
    return 0;

}

The error

The parent reads an image, encodes it and sends the encoded image data.

The child reads data in blocks of 4096. However in the second call to fread() where less than 4096 bytes are missing, it tries to read only the missing bytes: in my case the second call to frad should read 1027 bytes (115715%4096). In this second call it is not reading anything although the parent sends (or at least seems to send) all the data.

Why isn't fread() reading all the missing bytes?

I am working on this image:

There might be errors also on how I am trying to decode back the image so any help there would be appreciated too.


Working code for the old question

Note that after the answer of the original question I managed to send data properly but from the child to the parent (in the other direction). The working code for this is the following:

Parent:

#include <stdlib.h>
#include <unistd.h>//read
#include "opencv2/opencv.hpp"
#include <iostream>
#include <time.h>
#define BUFLEN 1024

int main(int argc, char *argv[])
{
    //file descriptor to the child process
    FILE *fp;
    cv::Mat frame;
    char temp[10] ={0};
    size_t bytes_read_tihs_loop = 0;
    size_t total_bytes_read = 0;
    unsigned short int elRead =0;
    size_t bytes2Copy = BUFLEN;
    //launch the child process with popen
    clock_t start, end;
    double cpu_time_used;

    start = clock();
    if ((fp = popen("/home/path/childProcess", "r")) == NULL)
    {
        //error
        return 1;
    }

    //read the number of btyes of encoded image data
    fgets(temp, 10, fp);
    //convert the string to int
    size_t bytesToRead = atoi((char*)temp);
    //some prints
    std::cout<<bytesToRead<<std::endl;

    //allocate memory where to store encoded iamge data that will be received
    u_char *buf = (u_char*)malloc(bytesToRead*sizeof(u_char));

    //initialize the number of bytes read to 0
    printf ("bytesToRead: %ld\n",bytesToRead);
    elRead = fread ( buf+total_bytes_read, bytes2Copy, bytesToRead/bytes2Copy, fp);
    total_bytes_read += elRead*bytes2Copy;
    bytes2Copy = bytesToRead-total_bytes_read;
    elRead = fread ( buf+total_bytes_read, bytes2Copy, bytesToRead/bytes2Copy, fp);
    total_bytes_read += elRead*bytes2Copy;
    printf("%lu bytes received over %lu expected\n", total_bytes_read, bytesToRead);
    printf("%lu final bytes read\n", total_bytes_read);
    pclose(fp);
    cv::namedWindow( "win", cv::WINDOW_AUTOSIZE );
    frame  = cv::imdecode(cv::Mat(1,total_bytes_read,0, buf), 0);
    cv::imshow("win", frame);
    end = clock();
    cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
    printf("time with %d buffer length: %f\n", BUFLEN, cpu_time_used);
    cv::waitKey(0);
    return 0;

}

Child:

#include <unistd.h> //STDOUT_FILENO
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;

#define BUFLEN 4096

int main(int argc, char *argv[])
{
    Mat frame;
    std::vector<uchar> buf;
    //read image as grayscale
    frame = imread("/home/path/test.png",0);
    //encode image and put data into the vector buf
    imencode(".png",frame, buf);
    //send the total size of vector to parent
    cout<<buf.size()<<endl;
    unsigned int written= 0;

    int i = 0;
    size_t toWrite = 0;
    //send until all bytes have been sent
    FILE * f = fdopen(STDOUT_FILENO, "w");
    while (written<buf.size())
    {
        //send the current block of data
        toWrite = BUFLEN < (buf.size()-written) ? BUFLEN : (buf.size()-written);
        //written += write(STDOUT_FILENO, buf.data()+written, toWrite);
        written += toWrite*fwrite ( buf.data()+written, toWrite, 1, f );
        i++;
    }
    return 0;

}

来源:https://stackoverflow.com/questions/55047561/buffered-read-write-of-image-data-between-parent-and-child-is-not-completing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!