Why doesn't the read() function return ? Did I use fork() incorrectly ?
before read()
after read()
num_of_pixels_received = 415
before read()
after read()
num_of_pixels_received = 420
before read()
after read()
num_of_pixels_received = 425
before read()
after read()
num_of_pixels_received = 430
before read()
after read()
num_of_pixels_received = 435
before read()
after read()
num_of_pixels_received = 440
before read()
*** Write process enters waiting status .....
- Code: Select all
// g++ -g -pedantic -Wall -Werror -Wextra -fsanitize=address host.cpp -o host `pkg-config --cflags --libs opencv`
#include <opencv2/core/core.hpp>
//#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <fstream> // std::ifstream, std::ofstream
#include <string>
#include <sys/wait.h>
#include <errno.h>
#include <cmath>
using namespace cv;
using namespace std;
//#define LOOPBACK 1
#define RGB2YUV 1
unsigned int image_width;
unsigned int image_height;
const unsigned int CHNL_NUM = 3;
const unsigned int RED_CHNL = 2;
const unsigned int GREEN_CHNL = 1;
const unsigned int BLUE_CHNL = 0;
const unsigned int STREAM_WIDTH = 128;
const unsigned int NUM_OF_BITS_PER_BYTE = 8;
const unsigned int PIXEL_VALUE_RANGE = 8; // number of bits occupied by [R, G, B] or [Y, U, V] respectively (8-bit unsigned integer for each components) , https://docs.microsoft.com/en-us/windows-hardware/drivers/display/yuv-rgb-data-range-conversions
const unsigned int NUM_OF_COMPONENTS_IN_A_PIXEL = 3; // input: [R, G, B] output:[Y, U, V]
const unsigned int PIXEL_NUM_THAT_FITS_STREAM_WIDTH = 5; // 128-bit stream can at most fits 5 pixels ((PIXEL_NUM_THAT_FITS_STREAM_WIDTH*NUM_OF_COMPONENTS_IN_A_PIXEL*PIXEL_VALUE_RANGE) bits = 120 bits), each pixels contains R, G, B which are encoded in 8 bits for each of the three color components
struct RGB_packet{
uint8_t R,G,B;
};
struct YUV_packet{
uint8_t Y,U,V;
};
struct YUV_packet* rgb2yuv(struct RGB_packet rgb_input) // convert rgb to yuv
{
unsigned char R = rgb_input.R;
unsigned char G = rgb_input.G;
unsigned char B = rgb_input.B;
int Y_temp, U_temp, V_temp;
struct YUV_packet *yuv_result = (YUV_packet *)malloc(image_width * image_height * sizeof(unsigned char) * NUM_OF_COMPONENTS_IN_A_PIXEL);
// https://en.wikipedia.org/wiki/YUV#Full_swing_for_BT.601
Y_temp = 77*R + 150*G + 29*B;
U_temp = -43*R - 84*G + 127*B;
V_temp = 127*R - 106*G - 21*B;
Y_temp = (Y_temp + 128) >> 8;
U_temp = (U_temp + 128) >> 8;
V_temp = (V_temp + 128) >> 8;
yuv_result->Y = Y_temp;
yuv_result->U = U_temp + 128;
yuv_result->V = V_temp + 128;
return yuv_result;
}
/*
Plain write() may not write all bytes requested in the buffer, so
allwrite() loops until all data was indeed written, or exits in
case of failure, except for EINTR. The way the EINTR condition is
handled is the standard way of making sure the process can be suspended
with CTRL-Z and then continue running properly.
The function has no return value, because it always succeeds (or exits
instead of returning).
The function doesn't expect to reach EOF either.
*/
int allwrite(int fd, unsigned char *buf, int len) {
int sent = 0;
int rc;
while (sent < len) {
rc = write(fd, buf + sent, len - sent);//fsync(fd);
if ((rc < 0) && (errno == EINTR))
continue;
if (rc < 0) {
perror("allwrite() failed to write");
exit(1);
}
if (rc == 0) {
fprintf(stderr, "Reached write EOF (?!)\n");
exit(1);
}
sent += rc;
}
return sent;
}
int main(int argc, char *argv[]) {
int fdr, fdw, rd, wr, rd_donebytes, wr_donebytes;
int wait_status; // for wait()
uint8_t *wr_buf, *rd_buf;
pid_t pid;
struct RGB_packet *tologic;
struct YUV_packet *fromlogic;
fdr = open("/dev/xillybus_read_128", O_RDONLY); // will change to /dev/xillybus_read_128
fdw = open("/dev/xillybus_write_128", O_WRONLY); // will change to /dev/xillybus_write_128
if ((fdr < 0) || (fdw < 0)) {
perror("Failed to open Xillybus device file(s)");
exit(1);
}
// READ in an image file
String imageName( "lena512color.tiff" ); // by default
if( argc > 1)
{
imageName = argv[1];
}
Mat image;
image = imread( imageName, IMREAD_COLOR ); // Read the file
if( image.empty() ) // Check for invalid input
{
cout << "Could not open or find the image" << std::endl ;
return -1;
}
else
{
image_width = image.size().width;
image_height = image.size().height;
}
namedWindow( "Original Image", WINDOW_AUTOSIZE );
imshow( "Original Image", image );
Mat rgbchannel[CHNL_NUM];
// The actual splitting.
split(image, rgbchannel);
namedWindow("Red", WINDOW_AUTOSIZE);
imshow("Red", rgbchannel[RED_CHNL]);
namedWindow("Green", WINDOW_AUTOSIZE);
imshow("Green", rgbchannel[GREEN_CHNL]);
namedWindow("Blue", WINDOW_AUTOSIZE);
imshow("Blue", rgbchannel[BLUE_CHNL]);
waitKey(0); // see all three split channels before feeding in the channel data to xillybus/RIFFA for hardware computation
vector<RGB_packet> vTo(image_width * image_height); // lena.tiff is sized as 3*512*512
tologic = vTo.data();
if (!tologic) {
fprintf(stderr, "Failed to allocate memory\n");
exit(1);
}
for(unsigned int pixel_index = 0; pixel_index < (image_width * image_height); pixel_index++)
{
tologic[pixel_index].R = *(rgbchannel[RED_CHNL].data + pixel_index);
tologic[pixel_index].G = *(rgbchannel[GREEN_CHNL].data + pixel_index);
tologic[pixel_index].B = *(rgbchannel[BLUE_CHNL].data + pixel_index);
}
pid = fork();
if (pid < 0) {
perror("Failed to fork()");
exit(1);
}
if (pid) {
close(fdr);
wr_donebytes = 0; // this variable includes the empty 8 bits for the MSB
unsigned int num_of_pixels_sent = 0; // this is actual pixels number already sent, does not include the empty 8 bits
//unsigned int if_index = 0;
unsigned int rgb_stream_index = 0;
uint8_t rgb_stream[STREAM_WIDTH/NUM_OF_BITS_PER_BYTE + 1]; // could accomodate 5 pixels
while (num_of_pixels_sent < image_width * image_height)
{
if(((image_width * image_height)-num_of_pixels_sent) >= PIXEL_NUM_THAT_FITS_STREAM_WIDTH)
{
// arrange the five pixels in the format as in https://i.imgur.com/mdJwk7J.png
//if_index++; //printf("if_index = %d\n\r", if_index);
//if(if_index == 3) break;
for(rgb_stream_index = 0; (rgb_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL-1)<((STREAM_WIDTH/NUM_OF_BITS_PER_BYTE)-1); rgb_stream_index=rgb_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL)
{
rgb_stream[rgb_stream_index] = tologic[(rgb_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_sent].R;
rgb_stream[rgb_stream_index+1] = tologic[(rgb_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_sent].G;
rgb_stream[rgb_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL-1] = tologic[(rgb_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_sent].B;
}
rgb_stream[STREAM_WIDTH/NUM_OF_BITS_PER_BYTE] = '\0'; // however, this NULL character is not sent across write()
rgb_stream[(STREAM_WIDTH/NUM_OF_BITS_PER_BYTE)-1] = 0; // remember that the eight most significant bits of the 128-bits stream are ignored by hardware logic
/*for(unsigned int j=0; j<(STREAM_WIDTH/NUM_OF_BITS_PER_BYTE); j++)
{
printf("rgb_stream[%d] = %d\n\r", j, rgb_stream[j]); //break;
}*/
wr_buf = rgb_stream; // write() writes wr_buf in first-in-first-out order, so rgb_stream[0] will be written first into fdw as the least significant byte
wr = allwrite(fdw, wr_buf, STREAM_WIDTH/NUM_OF_BITS_PER_BYTE); // this write() is 128-bits or 16 bytes which include the empty MSB 8 bits
//printf("wr = %d\n", wr);
num_of_pixels_sent = num_of_pixels_sent + PIXEL_NUM_THAT_FITS_STREAM_WIDTH;
//printf("num_of_pixels_sent = %d\n", num_of_pixels_sent);
}
else // the remaining pixels do not fill all five pixel slots for a 128-bit stream
{
for(rgb_stream_index = 0; (rgb_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL-1)<(((image_width * image_height)-num_of_pixels_sent)*NUM_OF_COMPONENTS_IN_A_PIXEL); rgb_stream_index=rgb_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL)
{
rgb_stream[rgb_stream_index] = tologic[(rgb_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_sent].R;
rgb_stream[rgb_stream_index+1] = tologic[(rgb_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_sent].G;
rgb_stream[rgb_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL-1] = tologic[(rgb_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_sent].B;
}
rgb_stream[((image_width * image_height)-num_of_pixels_sent)*NUM_OF_COMPONENTS_IN_A_PIXEL + 1] = '\0'; // however, this NULL character is not sent across write()
rgb_stream[((image_width * image_height)-num_of_pixels_sent)*NUM_OF_COMPONENTS_IN_A_PIXEL] = 0; // remember that the eight most significant bits of the 128-bits stream are ignored by hardware logic
/*for(unsigned int j=0; j<(((image_width * image_height)-num_of_pixels_sent)*NUM_OF_COMPONENTS_IN_A_PIXEL+1); j++)
{
printf("rgb_stream[%d] = %d\n\r", j, rgb_stream[j]);
}*/
wr_buf = rgb_stream; // this is a partially filled 128-bit stream (with less than 5 pixels)
wr = allwrite(fdw, wr_buf, ((image_width * image_height)-num_of_pixels_sent)*NUM_OF_COMPONENTS_IN_A_PIXEL+1);
//printf("wr = %d\n", wr);
break; // finish sending all (image_width * image_height) pixels
}
if ((wr < 0) && (errno == EINTR))
continue;
if (wr <= 0) {
perror("write() failed");
exit(1);
}
wr_donebytes += wr;
}
write(fdw, NULL, 0); // flush the write stream
close(fdw);
printf("*** Write process enters waiting status .....\n");
pid = wait(&wait_status);
printf("*** write process detects read process with pid %d was done ***\n", pid); // most probably write process will be done first, since FPGA computation takes a few clock cyles
return 0;
}
else {
close(fdw);
vector<YUV_packet> vFrom(image_width * image_height);
fromlogic = vFrom.data();
//printf("fromlogic[0].Y = %p \n", &fromlogic[0].Y);
if (!fromlogic) {
fprintf(stderr, "Failed to allocate memory\n");
exit(1);
}
//rd_buf = (uint8_t *) fromlogic; // yuv_stream
rd_donebytes = 0; // this variable includes the empty 8 bits for the MSB
unsigned int num_of_pixels_received = 0; // this is actual pixels number already received, does not include the empty 8 bits
unsigned int yuv_stream_index = 0;
uint8_t yuv_stream[STREAM_WIDTH/NUM_OF_BITS_PER_BYTE + 1]; // could accomodate 5 pixels
while (num_of_pixels_received < image_width * image_height) {
if(((image_width * image_height)-num_of_pixels_received) >= PIXEL_NUM_THAT_FITS_STREAM_WIDTH)
{
rd_buf = yuv_stream;
printf("before read() \n");
rd = read(fdr, rd_buf, STREAM_WIDTH/NUM_OF_BITS_PER_BYTE);
printf("after read() \n");
// For every five pixels (128 bits) received from hardware logic computation, print out the YUV values of all five pixels
/*for(yuv_stream_index = 0; yuv_stream_index<STREAM_WIDTH/NUM_OF_BITS_PER_BYTE; yuv_stream_index=yuv_stream_index+1)
{
printf("yuv_stream[%d] = %d\n", yuv_stream_index, yuv_stream[yuv_stream_index]);
}*/
yuv_stream[STREAM_WIDTH/NUM_OF_BITS_PER_BYTE] = '\0'; // this NULL character is only to act as "stop bit" for character array
//if(num_of_pixels_received == 300) break; // just to test if there is actually something being read, or returned from hardware
// store the calculated output YUV pixels into fromlogic such that we could reconstruct the image in its original dimension for visual display
for(yuv_stream_index = 0; (yuv_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL-1)<(STREAM_WIDTH/NUM_OF_BITS_PER_BYTE); yuv_stream_index=yuv_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL)
{
fromlogic[(yuv_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_received].Y = yuv_stream[yuv_stream_index];
fromlogic[(yuv_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_received].U = yuv_stream[yuv_stream_index+1];
fromlogic[(yuv_stream_index/NUM_OF_COMPONENTS_IN_A_PIXEL)+num_of_pixels_received].V = yuv_stream[yuv_stream_index+NUM_OF_COMPONENTS_IN_A_PIXEL-1];
}
num_of_pixels_received = num_of_pixels_received + PIXEL_NUM_THAT_FITS_STREAM_WIDTH;
printf("num_of_pixels_received = %d\n\r", num_of_pixels_received);
}
else // the remaining pixels do not fill all five pixel slots for a 128-bit stream
{
rd_buf = yuv_stream;
printf("before read in else. \n");
rd = read(fdr, rd_buf, image_width * image_height - num_of_pixels_received); // is a partially filled 128-bit stream (with less than 5 pixels)
//printf("break in else. \n");
break; // finish receiving all (image_width * image_height) pixels
}
if ((rd < 0) && (errno == EINTR))
continue;
if (rd < 0) {
perror("read() failed");
exit(1);
}
if (rd == 0) {
fprintf(stderr, "Reached read EOF!? Should never happen.\n");
exit(0);
}
rd_donebytes += rd;
}
printf("before for loop\n");
for (unsigned int i = 0; i < (image_width * image_height); i++) // check the perfomance of hardware with respect to software computation
{
#ifdef LOOPBACK
if( (tologic[i].R != fromlogic[i].Y) ||
(tologic[i].G != fromlogic[i].U) ||
(tologic[i].B != fromlogic[i].V) )
#elif RGB2YUV
uint8_t expected_Y = rgb2yuv(tologic[i])->Y;
uint8_t expected_U = rgb2yuv(tologic[i])->U;
uint8_t expected_V = rgb2yuv(tologic[i])->V;
if( (abs(expected_Y - fromlogic[i].Y) > 1) ||
(abs(expected_U - fromlogic[i].U) > 1) ||
(abs(expected_V - fromlogic[i].V) > 1) ) // rgb2yuv conversion hardware tolerance fails by more than 1 compared to software computation
#endif
{
printf("********************************* Attention *************************************\n\r");
printf("R:%d G:%d B:%d \n\r", tologic[i].R, tologic[i].G, tologic[i].B);
printf("Y:%d U:%d V:%d \n\r", fromlogic[i].Y, fromlogic[i].U, fromlogic[i].V);
printf("expected_Y:%d expected_U:%d expected_V:%d \n\r", expected_Y, expected_U, expected_V);
break; // just for troubleshooting
//exit(1);
}
}
close(fdr);
printf("*** Read process enters waiting status .....\n");
pid = wait(&wait_status);
printf("*** read process detects write process with pid %d was done ***\n", pid); // most probably write process will be done first, since FPGA computation takes a few clock cyles
return 0;
}
/*pid = wait(&wait_status);
printf("*** Parent detects process %d is done ***\n", pid);
printf("*** Parent exits ***\n");*/
exit(0);
}