问题
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