<< return to Pixycam.com

Snapshot with libpixyusb?

Hello Maxime,
Hmm, I’m not sure what’s going on. As an experiment, if you take the data in the pixels array and copy it into new memory, does this fix the issue?

Edward

Hello,

I did not tried this. It can be the solution. Or maybe to copy the cv::Mat image inside another cv::Mat.

I managed to find a solution that works: I save the pixels into “data” in RGB order and then convert it to RGB (it is default format used by openCV from what I understood).
I link the final commented code. If someone manage to fix the problem in a more elegant way, please let me know, I would be very interested to know how to fix it.

Good luck buddies.

Maxime

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <math.h>  
#include <pixy.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <ctime>
#include <iostream>
#include <sstream>
#include <cerrno>
// I put this include because I saw some guy telling to use this lib for the management of 
// argv[2] but during my tests I found that it is actually not needed. I leave it here
// in case my tests where not complete enough.
#if 0
	#include <limits>
#endif

// Maximum number of pictures to be taken
#define NB_PICT_MAX 30

// INSTRUCTION TO COMPILE AFTER INSTALLING OPENCV AND PIXY AS EXPLAINED ON THE WEBSITES OF THESE LIBRARIES:
// g++ -I/usr/include/libusb-1.0/  -I/usr/local/include -L/usr/local/lib/ `pkg-config --cflags --libs opencv` snapshot.cpp -o snapshot -Wall -lpixyusb -lusb-1.0 -lboost_system -lboost_thread -lboost_date_time -lboost_chrono
// The program has been compiled in the following folder. Modifications might be required if you want to compile it in another folder.
// /home/pi/pixy/src/host

// SYSTEM INFO
// g++ --version
// g++ (Raspbian 4.9.2-10) 4.9.2

// Libraries openCV and Pixy installed from my home folder
// /home/pi/opencv
// /home/pi/pixy

// COMMAND TO LAUNCH THE PROGRAM
// sudo ./snapshot genericNameForPictures nbOfPicturesToBeTaken

using namespace cv;
using namespace std;

// Not very clean to declare prototypes like this. Improvements to be done: seperate the main from the other functions and include the corresponsind ".hpp" file
void interpolateBayer(unsigned int width, unsigned int x, unsigned int y, unsigned char *pixel, unsigned int &r, unsigned int &g, unsigned int &b);
Mat renderBA81(uint8_t renderFlags, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame);
Mat getImage();

int main(int argc, char * argv[])
{
    int pixy_init_status;
    int return_value;
    int i;
    int nbPict;
    long conv;
    char *p;
    int32_t response;
    Mat image;
	string fileName;
	// Path where to save images
	string path("/home/pi/pixy/calib/");
	// Extension to use to save images (check openCV imwrite() function to learn more)
	string fileExtension(".png");
	string genericName;
	stringstream number;
	
	errno = 0;
	
	// Check that there are the right number of arguments (1 generic name to save the pictures and the number of pictures to take)
	if (argc < 3) 
	{
	
		// Display an error message and stop the program
		cout << "Not enough input arguments. First argument is the generic name of the pictures (without parth or extension) and the second argument is the number of pictures to be taken." << endl;
		return 1;
		
	} else 
	{
		// Store the arguments into variables
		genericName = argv[1];
		conv = strtol(argv[2], &p, 10);
	
	}
	
	// Check for errors: e.g., the string is not empty or the integer is larger than NB_PICT_MAX
	if (errno != 0 || *p != '\0' || conv > NB_PICT_MAX || conv == 0)
	{
		
		// Display error message
		cout << "Second argument must be an integer between 1 and " << NB_PICT_MAX << endl;
		return 2;
		
	} else 
	{
		
		// No error: save number of pictures to take inside an integer (nbPict)
		nbPict = conv;
		cout << "The program will take and save " << nbPict << " pictures." << endl;
		
	}
	
	// Initilize Pixy
    pixy_init_status = pixy_init();

    if(!pixy_init_status == 0)
    {
		
        printf("pixy_init(): ");
        pixy_error(pixy_init_status);
        return pixy_init_status;
        
    }
	cout << "Init Pixy..." << endl;
	
	// Stop the Pixy
	return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
	if(return_value != 0)
	{
		cout << "Can not stop Pixy" << endl;
		return return_value;
	}
	//cout << "Stop Pixy: " << return_value << endl;
	
	// Loop from 1 to nbPict
	for(i=1; i<=nbPict; i++)
	{
		// Generate the name for the picture to be taken and then saved
		number.str("");
		number << i;
		fileName = path + genericName + number.str() + fileExtension;
		
		// Take a picture with Pixy and put it in the required form to be displayed/saved
		image = getImage();
		
		// Check that Pixy actually sent an image
		if( image.empty() == 1)
		{
			
			cout << "No image received from Pixy" << endl;
			return 3;
			
		}
		
				
		// Display the image and wait for the user to press any key
		namedWindow( "Snapshot", WINDOW_AUTOSIZE );
		imshow("Snapshot", image);
		waitKey(0);

		// Save the image
		try
		{
			
			imwrite(fileName, image);
			
		}
		catch (runtime_error& ex)
		{
			
			fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
			return 4;
			
		}
		
		// Message to tell the user that the picture has been saved
		cout << "Successfully saved " << fileName << " file." << endl;
		
    }
    
    return 0;
}

Mat getImage()
{
	// Asks Pixy to send an image and gives this image to renderBA81
    unsigned char *pixels;
    int32_t response, fourcc;
    int8_t renderflags;
    int return_value;
    uint16_t rwidth, rheight;
    uint32_t  numPixels;
	Mat image;
	char fourcc_code[5];
	
	// Runs the Pixy to make it put a frame inside its video buffer
    return_value = pixy_command("run", END_OUT_ARGS, &response, END_IN_ARGS);
    if(return_value != 0)
	{
		
		cout << "Can run stop Pixy. Returned value : " << return_value << endl;
		return image;
		
	}
	//cout << "Run Pixy getImage: " << return_value << endl;
    
    // Stops the Pixy before asking it to send the frame stored inside its video buffer
    return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
    if(return_value != 0)
	{
		
		cout << "Can not stop Pixy. Returned value : " << return_value << endl;
		return image;
		
	}
	//cout << "Stop Pixy getImage: " << return_value << endl;
    
    // Asks the Pixy to send a frame
    return_value = pixy_command("cam_getFrame",  // String id for remote procedure
								 /* Mx, or "mode x" where x is either:
									0: full resolution mode (1280x800) at 25 frames/sec
									1: quarter resolution mode (640x400) at 50 frames/sec -- the imager bins/averages the pixels
																 
									Ry, or "resolution y" where y is either:
									0: 1280x800 resolution no pixel binning/averaging
									1: 640x400 resolution no pixel binning/averaging (only available in mode 1)
									2: 320x200 resolution with pixel binning/averaging (also only available in mode 1) */
								 // mode: CAM_GRAB_M0R0 (0x00), CAM_GRAB_M1R1 (0x11), CAM_GRAB_M1R2 (0x21)
                                 0x01, 0x11,
                                 0x02, 155,        // xoffset
                                 0x02, 90,         // yoffset
                                 // Memory size = 73728 bytes (72K) so width * height must be lower or equel to this
                                 0x02, 330,       // width
                                 0x02, 220,       // height
                                 0,            // separator
                                 &response, &fourcc, &renderflags, &rwidth, &rheight, &numPixels, &pixels, 0);
    
    // Check that the Pixy successfully returned a frame
    if(return_value != 0)
	{
		
		cout << "Can not get frame from Pixy. Returned value : " << return_value << endl;
		return image;
		
	}

	// Convert fourcc code into string
	fourcc_code[0] = fourcc & 0xff;
	fourcc_code[1] = (fourcc >> 8) & 0xff;
	fourcc_code[2] = (fourcc >> 16) & 0xff;
	fourcc_code[3] = (fourcc >> 24) & 0xff;
	fourcc_code[4] = '\0';
	
	// Display the different values returned by the Pixy
	//cout << "Cam_GetFrame Pixy: " << return_value << endl;
	cout << "Response returned by Pixy : " << response << ", fourcc : " << fourcc_code << ", renderflags : " << renderflags << ", width : " << rwidth << ", height : " << rheight << ", numPixels : " << numPixels << endl;

    // Render frame sent by Pixy
    image = renderBA81(renderflags,rwidth,rheight,numPixels,pixels);

    return image;
}

inline void interpolateBayer(uint16_t width, uint16_t x, uint16_t y, uint8_t *pixel, uint8_t* r, uint8_t* g, uint8_t* b)
{
	// Smear the pixel depending on parity on abscissa and ordinate (parity gives color -> see Bayer matrix for more info about it)
    if (y&1)
    {
        if (x&1)
        {
            *r = *pixel;
            *g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
            *b = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
        }
        else
        {
            *r = (*(pixel-1)+*(pixel+1))>>1;
            *g = *pixel;
            *b = (*(pixel-width)+*(pixel+width))>>1;
        }
    }
    else
    {
        if (x&1)
        {
            *r = (*(pixel-width)+*(pixel+width))>>1;
            *g = *pixel;
            *b = (*(pixel-1)+*(pixel+1))>>1;
        }
        else
        {
            *r = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
            *g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
            *b = *pixel;
        }
    }

}

Mat renderBA81(uint8_t renderFlags, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame)
{
	// Receives a frame, call the interpolateBayer function on every pixel of the frame expect for the pixels on the borders (left, right, top and bottom).
	// Data as the folling size : frame_width-2 x frame_height-2
    uint16_t x, y;
    uint8_t r, g, b;
    Mat imageRGB;
    Mat imageBGR;
	uchar data[3*((height-2)*(width-2))];
    uint m = 0;

	// Skip first line
    frame += width;
    
    // Take each pixel of the frame and perform on it the Bayer interpolation and save the obtained Red, Blue and Green pixels into data.
    for (y=1; y<height-1; y++)
    {
		
        frame++;
        for (x=1; x<width-1; x++, frame++)
        {
			
            interpolateBayer(width, x, y, frame, &r, &g, &b);
			// RGB order and conversion to BGR because of memory corruption if directly wrote into BGR and returned. Can't fix the problem in another way.
			// N.B.: BGR because it is default format for openCV
            data[m++] = r;
            data[m++] = g;
            data[m++] = b;
        
        }
        
        frame++;
    
    }
	
	// "Cast" data to cv::Mat
    imageRGB =  Mat(height - 2,width -2, CV_8UC3, data);
	
	// Convert matrix from RGB to BGR
    cvtColor (imageRGB,imageBGR,CV_RGB2BGR);

    return imageBGR;
}