我正在尝试从子进程(通过从父级调用popen
生成)发送图像到父级进程。
该图像是灰度png
图像。它使用OpenCV库打开,并使用同一库的imencode
函数进行编码。因此,将所得的编码数据存储到类型为std::vector
的{{1}}结构中,即以下代码中的uchar
向量。
首先,孩子发送父母需要的以下图像信息:
buf
向量的大小:需要此信息,以便父级将分配相同大小的缓冲区,以将其将从图像接收的图像信息写入其中。儿童。分配如下执行(在这种情况下,buf
是用于接收数据的数组,而不是包含编码数据的向量):
buf
这些数据由子级使用u_char *buf = (u_char*)malloc(val*sizeof(u_char));
写入标准输出,并由父级使用cout
系统调用读取。
这些信息已正确发送和接收,因此到目前为止没有问题。
子级使用fgets
系统调用将编码数据(即向量buf
中包含的数据)写入标准输出,而父级则使用write
返回的文件描述符读取数据。使用popen
系统调用读取数据。
数据写入和读取在while循环中以read
字节的块进行。编写行如下:
4096
其中written += write(STDOUT_FILENO, buf.data()+written, s);
告诉您在标准输出上写。
STDOUT_FILENO
返回指向向量结构内部使用的数组中第一个元素的指针。
buf.data()
存储到现在为止已写入的字节数,它用作索引。 written
是s
每次尝试发送的字节数(4096
)。
write
返回实际已写入的字节数,该字节数用于更新write
。
数据读取非常相似,它由以下行执行:
written
bytes_read = read(fileno(fp), buf+total_bytes, bytes2Copy);
告诉从哪里读取数据(fileno(fp)
是fp
返回的文件描述符)。 popen
是存储接收到的数据的数组,而buf
是到目前为止所读取的字节数,因此将其用作索引。 total_bytes
是期望接收的字节数:bytes2Copy
(即BUFLEN
)枯萎,或者最后一个数据块剩余的数据(例如,总字节数为{ {1}},然后在4096
个字节的1块之后,又将5000
的另一个块)。
请考虑以下示例。以下是使用4096
5000-4096
和上面打开的过程对应于以下内容:
popen
孩子读取图像,对其进行编码,然后将尺寸(大小,#行,#cols)发送给父对象,然后再发送编码后的图像数据。
父级首先读取尺寸(没有尺寸),然后开始读取数据。每次迭代都读取#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;
}
个字节的数据。但是,当缺少少于#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;
}
个字节时,它将尝试仅读取缺少的字节:在我的情况下,最后一步应该读取4096
个字节(4096
),而不是读取所有它们的读数为15。
我在前两次迭代中打印的是:
1027
115715%4096
为什么不读取所有丢失的字节?
在我尝试对图像进行解码时可能也会出错,因此也将不胜感激。
编辑
在我看来,与某些建议相反,该问题与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
或\n
的存在无关。
实际上,当我用以下几行打印接收为整数的数据时:
\r
我在数据中间看到\0
,for (int ii=0; ii<val; ii++)
{
std::cout<<(int)buf[ii]<< " ";
}
和0
值(上述字符的ASCII值),因此我认为这不是问题。 / p>
答案 0 :(得分:2)
fgets(temp, 10, fp);
...
read(fileno(fp), ...)
这可能行不通。
stdio
例程是缓冲的。缓冲区由实现控制。 fgets(temp, 10, fp);
将从文件中读取未知数量的字节并将其放入缓冲区。这些字节将再也不会被低级文件IO看到。
您永远都不会在两种IO样式中都使用相同的文件。使用stdio
或使用低级IO进行所有操作。到目前为止,第一种选择是最简单的,只需将read
替换为fread
。
如果出于某种邪恶的原因而只知道黑暗的邪恶力量,而您想要保留两种IO风格,则可以在执行任何其他操作之前通过调用setvbuf(fp, NULL, _IOLBF, 0)
进行尝试。我从来没有这样做过,不能保证使用这种方法,但是他们说它应该起作用。我看不出有任何理由使用它。
在一个可能不相关的地方,请注意,您的阅读循环在终止条件上有一些逻辑,不太容易理解并且可能无效。读取文件的正常方式大致如下:
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
,请重试,因此修改后的条件看起来像
while (left > 0 &&
(((got=read(file, buf+total, min(chunk_size, left))) > 0) ||
(got < 0 && errno == EINTR))) {
但是这时可读性开始受到影响,您可能希望将其拆分为单独的语句。
答案 1 :(得分:0)
您正在将二进制数据写入标准输出,该输出需要文本。可以添加或删除换行符(\n
)和/或返回字符(\r
),具体取决于文本文件中行尾的系统编码。由于缺少字符,因此系统似乎正在删除这两个字符之一。
您需要将数据写入以二进制模式打开的文件,并且应该以二进制形式读取文件。
答案 2 :(得分:0)
更新后的答案
我不是世界上最擅长C ++的人,但这可以使您有一个合理的起点。
parent.cpp
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include "opencv2/opencv.hpp"
int main(int argc, char *argv[])
{
// File descriptor to the child process
FILE *fp;
// Launch the child process with popen
if ((fp = popen("./child", "r")) == NULL)
{
return 1;
}
// Read the number of bytes of encoded image data
std::size_t filesize;
fread(&filesize, sizeof(filesize), 1, fp);
std::cout << "Filesize: " << filesize << std::endl;
// Allocate memory to store encoded image data that will be received
std::vector<uint8_t> buffer(filesize);
int bufferoffset = 0;
int bytesremaining = filesize;
while(bytesremaining>0)
{
std::cout << "Attempting to read: " << bytesremaining << std::endl;
int bytesread = fread(&buffer[bufferoffset],1,bytesremaining,fp);
bufferoffset += bytesread;
bytesremaining -= bytesread;
std::cout << "Bytesread/remaining: " << bytesread << "/" << bytesremaining << std::endl;
}
pclose(fp);
// Display that image
cv::Mat frame;
frame = cv::imdecode(buffer, -CV_LOAD_IMAGE_ANYDEPTH);
cv::imshow("win", frame);
cv::waitKey(0);
}
child.cpp
#include <cstdio>
#include <cstdint>
#include <vector>
#include <fstream>
#include <cassert>
#include <iostream>
int main()
{
std::FILE* fp = std::fopen("image.png", "rb");
assert(fp);
// Seek to end to get filesize
std::fseek(fp, 0, SEEK_END);
std::size_t filesize = std::ftell(fp);
// Rewind to beginning, allocate buffer and slurp entire file
std::fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> buffer(filesize);
std::fread(buffer.data(), sizeof(uint8_t), buffer.size(), fp);
std::fclose(fp);
// Write filesize to stdout, followed by PNG image
std::cout.write((const char*)&filesize,sizeof(filesize));
std::cout.write((const char*)buffer.data(),filesize);
}
原始答案
有两个问题:
您的while循环从子进程中写入数据是不正确的:
while (written<buf.size())
{
//send the current block of data
written += write(STDOUT_FILENO, buf.data()+written, s);
i++;
}
想象一下,您的图像是4097字节。您将在循环中第一次写入4096个字节,然后在缓冲区中仅剩1个字节的情况下尝试在第二遍写入4096个字节(即s
)。
您应写入4096和缓冲区中剩余字节中的较小者。
发送文件的宽度和高度没有意义,它们已经被编码在要发送的PNG文件中。
在子级中调用imread()
将磁盘上的PNG文件转换为cv::Mat
,然后再调用imencode()
将其转换回PNG并发送给父级是没有意义的。只需open()
并以二进制文件形式读取文件并将其发送-它已经是PNG文件。
我认为您要清楚发送PNG文件还是纯像素数据。 PNG文件将具有:
仅像素数据文件将具有: