Введение и общая цель
Я пытаюсь отправить изображение из дочернего процесса (созданного путем вызова popen
из родительского процесса) в родительский процесс.
Изображение представляет собой изображение в градациях серого png
.Он открывается библиотекой OpenCV и кодируется с использованием функции imencode
той же библиотеки.Таким образом, полученные закодированные данные сохраняются в структуре std::vector
типа uchar
, а именно в buf
в приведенном ниже коде.
Нет ошибок при отправке предварительной информации об изображении
Сначала ребенок отправляет следующую информацию об изображении, необходимую родителю:
размер buf
вектор, содержащий закодированные данные: этот фрагмент информации необходим для того, чтобы родительский объект выделил буфер того же размера, в который будет записываться информация об изображении, которую он получит от дочернего элемента.Распределение выполняется следующим образом (buf
в этом случае - массив, используемый для принятых данных, а не вектор, содержащий закодированные данные):
u_char *buf = (u_char*)malloc(val*sizeof(u_char));
- количество строк исходного изображения: необходимо дляродительский элемент для декодирования изображения после получения всех данных;
- количество столбцов исходного изображения: необходим для родительского декодирования изображения после получения всех данных.
Эти данные записываются дочерним элементом в стандартный вывод с использованием cout
и считываются родительским с помощью системного вызова fgets
.
Эта информация корректно отправляется и принимается, поэтому до сих пор проблем не было .
Отправка данных изображения
Дочерний объект записывает закодированные данные (т.е.данные, содержащиеся в векторе buf
), выводятся на стандартный вывод с использованием системного вызова write
, в то время как родительский файл использует дескриптор файла, возвращаемый popen
, для чтения данных.Данные считываются с помощью системного вызова read
.
Запись и чтение данных выполняется в блоках по 4096
байтов внутри циклов while.Строка записи следующая:
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
(то есть 4096
) или для последнего блока данных оставшиеся данные (если, например, общее количество байтов 5000
, то после 1 блока4096
байт ожидается еще один блок 5000-4096
.
Код
Рассмотрим этот пример.Ниже приведен процесс, запускающий дочерний процесс с 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;
}
, и процесс, открываемый выше, соответствует следующему:
#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;
}
Ошибка
Ребенок читает изображение, кодирует его и отправляет сначала размеры (размер, #rows, #cols) родителю, а затем данные закодированного изображения.
Родитель сначала читает размеры (без проблем с этим), затем начинается чтение данных.Данные читаются 4096
байтов на каждой итерации.Однако когда пропущено менее 4096
байт, он пытается прочитать только отсутствующие байты: в моем случае последний шаг должен прочитать 1027
байт (115715%4096
), но вместо чтения всех из них он просто читает `15.
Что я напечатал за последние две итерации:
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
, 10
и 13
значения (значения ASCII вышеупомянутых символов) в середине данных, так что это заставляет меня думать, что это не проблема.