问题描述
我正在尝试从子进程(通过从父级调用popen
生成)发送图像到父级进程.
I am trying to send an image from a child process (generated by calling popen
from the parent) to the parent process.
图像是灰度png
图像.它使用OpenCV库打开,并使用同一库的imencode
函数进行编码.因此,将所得的编码数据存储到类型为uchar
的std::vector
结构中,即下面的代码中的buf
向量.
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 buf
vector in the code below.
首先,孩子发送父母需要的以下图像信息:
First the child sends the following image information needed by the parent:
- 包含编码数据的
-
大小:需要此信息,以便父级将分配相同大小的缓冲区,以将其将从子级接收到的图像信息写入其中.分配如下执行(在这种情况下,
buf
是用于接收数据的数组,而不是包含编码数据的向量):
buf
向量的size of the
buf
vector containing the encoded data: this piece of information is needed so that the parent will allocate a buffer of the same size where to write the image information that it will receive from the child. Allocation is performed as follows (buf
in this case is the array used to received data not the vector containing the encoded data):
u_char *buf = (u_char*)malloc(val*sizeof(u_char));
这些数据由子级使用cout
写入标准输出,并由父级使用fgets
系统调用读取.
These data are written by the child on the standard output using cout
and read by the parent using fgets
system call.
这些信息已正确发送和接收,因此到目前为止没有问题.
This pieces of information are correctly sent and received so no problem until now.
子级使用write
系统调用将编码数据(即向量buf
中包含的数据)写入标准输出,而父级则使用popen
返回的文件描述符读取数据.使用read
系统调用读取数据.
The child writes the encoded data (i.e. the data contained in the vector buf
) to the standard output using write
system call while the parent uses the file-descriptor returned by popen
to read the data. Data is read using read
system call.
数据写入和读取在while循环内以4096
个字节的块进行.编写行如下:
Data writing and reading is performed in blocks of 4096
bytes inside while loops. The writing line is the following:
written += write(STDOUT_FILENO, buf.data()+written, s);
其中,STDOUT_FILENO
指示在标准输出上写. buf.data()
将指针返回到向量结构内部使用的数组中第一个元素的指针.written
存储到现在为止已写入的字节数,并用作索引. s
是write
每次尝试发送的字节数(4096
).write
返回实际已写入的字节数,该字节数用于更新written
.
where STDOUT_FILENO
tells to write on standard output. buf.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. s
is the number of bytes (4096
) that write
will try to send each time.write
returns the number of bytes 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:
bytes_read = read(fileno(fp), buf+total_bytes, bytes2Copy);
fileno(fp)
告诉从哪里读取数据(fp
是popen
返回的文件描述符). buf
是存储接收到的数据的数组,而total_bytes
是直到现在为止读取的字节数,因此将其用作索引. bytes2Copy
是预期要接收的字节数:BUFLEN
(即4096
)凋零,或者最后一个数据块剩余的数据(例如,如果总字节为5000
,则在1个块之后) 4096
个字节的数据,则需要另一个5000-4096
块.)
fileno(fp)
is telling from where to read data (fp
is the filedescriptor returned by popen
). buf
is the array where received data is stored and total_bytes
are the number of bytes read until now so it is used as index. bytes2Copy
is the number of bytes expected to be received: it is wither 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).
请考虑以下示例.以下是使用popen
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>
#define BUFLEN 4096
int main(int argc, char *argv[])
{
//file descriptor to the child process
FILE *fp;
cv::Mat frame;
char temp[10];
size_t bytes_read_tihs_loop = 0;
size_t total_bytes_read = 0;
//launch the child process with popen
if ((fp = popen("/path/to/child", "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);
//allocate memory where to store encoded iamge data that will be received
u_char *buf = (u_char*)malloc(bytesToRead*sizeof(u_char));
//some prints
std::cout<<bytesToRead<<std::endl;
//initialize the number of bytes read to 0
bytes_read_tihs_loop=0;
int bytes2Copy;
printf ("bytesToRead: %ld\n",bytesToRead);
bytes2Copy = BUFLEN;
while(total_bytes_read<bytesToRead &&
(bytes_read_tihs_loop = read(fileno(fp), buf+total_bytes_read, bytes2Copy))
)
{
//bytes to be read at this iteration: either 4096 or the remaining (bytesToRead-total)
bytes2Copy = BUFLEN < (bytesToRead-total_bytes_read) ? BUFLEN : (bytesToRead-total_bytes_read);
printf("%d btytes to copy\n", bytes2Copy);
//read the bytes
printf("%ld bytes read\n", bytes_read_tihs_loop);
//update the number of bytes read
total_bytes_read += bytes_read_tihs_loop;
printf("%lu total bytes read\n\n", total_bytes_read);
}
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);
return 0;
}
和上面打开的过程对应于以下内容:
and the process opened by the above corresponds to the following:
#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("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
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);
i++;
}
return 0;
}
错误
孩子读取图像,对其进行编码,然后首先将尺寸(大小,#行,#cols)发送给父对象,然后再发送编码后的图像数据.
The error
The child reads an image, encodes it and sends first the dimensions (size, #rows, #cols) to the parent and then the encoded image data.
父级首先读取尺寸(没有尺寸问题),然后开始读取数据.每次迭代读取数据4096
个字节.但是,当缺少少于4096
个字节时,它将尝试仅读取缺少的字节:在我的情况下,最后一步应该读取1027
个字节(115715%4096
),但是与其读取所有字节,不如直接读取`15
The parent reads first the dimensions (no prob with that), then it starts reading data. Data is read 4096
bytes at each iteration. However when less than 4096
bytes are missing, it tries to read only the missing bytes: in my case the last step should read 1027
bytes (115715%4096
), but instead of reading all of them it just reads `15.
我在前两次迭代中打印的是:
What I got printed for the last two iterations is:
4096 btytes to copy
1034 bytes read
111626 total bytes read
111626 bytes received over 115715 expected
111626 final bytes read
OpenCV(4.0.0-pre) Error: Assertion failed (size.width>0 && size.height>0) in imshow, file /path/window.cpp, line 356
terminate called after throwing an instance of 'cv::Exception'
what(): OpenCV(4.0.0-pre) /path/window.cpp:356: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'imshow'
Aborted (core dumped)
为什么read
不读取所有丢失的字节?
Why isn't read
reading all the missing bytes?
我正在处理此图像:
在我尝试对图像进行解码的过程中也可能会出现错误,因此也将不胜感激.
There might be errors also on how I am trying to decode back the image so any help there would be appreciated too.
编辑
在我看来,与某些建议相反,该问题与\n
或\r
或\0
的存在无关.
In my opinion as opposed to some suggestions the problem is not related to the presence of \n
or \r
or \0
.
实际上,当我用以下几行打印接收为整数的数据时:
In fact when I print data received as integer with the following lines:
for (int ii=0; ii<val; ii++)
{
std::cout<<(int)buf[ii]<< " ";
}
我在数据中间看到了0
,10
和13
值(上述字符的ASCII值),所以我认为这不是问题.
I see 0
, 10
and 13
values (the ASCII values of the above mentioned characters) in the middle of data so this makes me think it is not the problem.
推荐答案
fgets(temp, 10, fp);
...
read(fileno(fp), ...)
这可能行不通.
stdio
例程是缓冲的.缓冲区由实现控制. fgets(temp, 10, fp);
将从文件中读取未知数目的字节并将其放入缓冲区.这些字节将再也不会被低级文件IO看到.
stdio
routines are buffered. Buffers are controlled by the implementation. fgets(temp, 10, fp);
will read an unknown number of bytes from the file and put it in a buffer. These bytes will never be seen by low level file IO again.
您永远都不会在两种IO样式中都使用相同的文件.使用stdio
可以执行任何操作,或者使用低级IO可以执行任何操作.到目前为止,第一个选项是最简单的,只需将read
替换为fread
.
You never, ever, use the same file with both styles of IO. Either do everything with stdio
, or do everything with low-level IO. The first option is the easiest by far, you just replace read
with fread
.
如果出于某种邪恶的原因而只知道黑暗的邪恶力量,而您想要保留两种IO风格,则可以在执行其他任何操作之前通过调用setvbuf(fp, NULL, _IOLBF, 0)
进行尝试.我从来没有这样做过,不能保证使用这种方法,但是他们说它应该起作用.我看不出有任何理由使用它.
If for some ungodly reason known only to the evil forces of darkness you want to keep both styles of IO, you can try that by calling setvbuf(fp, NULL, _IOLBF, 0)
before doing anything else. I have never done that and cannot vouch for this method, but they say it should work. I don't see a single reason to use it though.
在可能不相关的地方,请注意,您的阅读循环在终止条件中具有一些逻辑,不太容易理解并且可能无效.读取文件的正常方式大致如下:
On a possibly unrelated, note, your reading loop has some logic in its termination condition that is not so easy to understand and could be invalid. The normal way to read a file looks approximately as follows:
left = data_size;
total = 0;
while (left > 0 &&
(got=read(file, buf+total, min(chunk_size, left))) > 0) {
left -= got;
total += got;
}
if (got == 0) ... // reached the end of file
else if (got < 0) ... // encountered an error
更正确的方法是got < 0 && errno == EINTR
,然后重试,因此修改后的条件看起来像
The more correct way would be to try again if got < 0 && errno == EINTR
, so the modified condition could look like
while (left > 0 &&
(((got=read(file, buf+total, min(chunk_size, left))) > 0) ||
(got < 0 && errno == EINTR))) {
但是这时可读性开始受到损害,您可能希望将其拆分为单独的语句.
but at this point readability starts to suffer and you may want to split this in separate statements.
这篇关于为什么缺少缺少的块时,读取系统调用会停止读取?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!