initial verion
This commit is contained in:
commit
edbc4d68bc
27
Makefile
Normal file
27
Makefile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Makefile for Basler pylon sample program
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
# The program to build
|
||||||
|
NAME := minimal_pylon
|
||||||
|
|
||||||
|
# Installation directories for pylon
|
||||||
|
PYLON_ROOT ?= /opt/pylon
|
||||||
|
|
||||||
|
# Build tools and flags
|
||||||
|
LD := $(CXX)
|
||||||
|
CPPFLAGS := $(shell $(PYLON_ROOT)/bin/pylon-config --cflags)
|
||||||
|
CXXFLAGS := #e.g., CXXFLAGS=-g -O0 for debugging
|
||||||
|
LDFLAGS := $(shell $(PYLON_ROOT)/bin/pylon-config --libs-rpath)
|
||||||
|
LDLIBS := $(shell $(PYLON_ROOT)/bin/pylon-config --libs)
|
||||||
|
|
||||||
|
# Rules for building
|
||||||
|
all: $(NAME)
|
||||||
|
|
||||||
|
$(NAME): $(NAME).o
|
||||||
|
$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||||
|
|
||||||
|
$(NAME).o: $(NAME).cpp
|
||||||
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(NAME).o $(NAME)
|
237
minimal_pylon.cpp
Normal file
237
minimal_pylon.cpp
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Grab_MultipleCameras.cpp
|
||||||
|
/*
|
||||||
|
Note: Before getting started, Basler recommends reading the "Programmer's Guide" topic
|
||||||
|
in the pylon C++ API documentation delivered with pylon.
|
||||||
|
If you are upgrading to a higher major version of pylon, Basler also
|
||||||
|
strongly recommends reading the "Migrating from Previous Versions" topic in the pylon C++ API documentation.
|
||||||
|
|
||||||
|
This sample illustrates how to grab and process images from multiple cameras
|
||||||
|
using the CInstantCameraArray class. The CInstantCameraArray class represents
|
||||||
|
an array of instant camera objects. It provides almost the same interface
|
||||||
|
as the instant camera for grabbing.
|
||||||
|
The main purpose of the CInstantCameraArray is to simplify waiting for images and
|
||||||
|
camera events of multiple cameras in one thread. This is done by providing a single
|
||||||
|
RetrieveResult method for all cameras in the array.
|
||||||
|
Alternatively, the grabbing can be started using the internal grab loop threads
|
||||||
|
of all cameras in the CInstantCameraArray. The grabbed images can then be processed by one or more
|
||||||
|
image event handlers. Please note that this is not shown in this example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pylon/PylonIncludes.h>
|
||||||
|
#include "stitchimage.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <unistd.h> // for read(), STDIN_FILENO
|
||||||
|
#include <termios.h> // for tcgetattr(), tcsetattr()
|
||||||
|
#include <sys/select.h> // for select()
|
||||||
|
|
||||||
|
using namespace Pylon;
|
||||||
|
using namespace GenApi;
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
typedef high_resolution_clock Time;
|
||||||
|
typedef milliseconds ms;
|
||||||
|
typedef duration<float> fsec;
|
||||||
|
|
||||||
|
int main( int argc, char* argv[] ) {
|
||||||
|
uint32_t frameCount = 100;
|
||||||
|
size_t maxCameras = 1;
|
||||||
|
int exitCode = 0;
|
||||||
|
bool stop_request = false;
|
||||||
|
int camCount = 0;
|
||||||
|
int framerate = 20;
|
||||||
|
uint32_t quality = 50;
|
||||||
|
int cWidth = 2000; //2592;
|
||||||
|
int cHeight = 2000; //2048;
|
||||||
|
int camIndex = 0;
|
||||||
|
string errorMessage = "";
|
||||||
|
String_t filename = "_TestVideo.mp4";
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
if ((arg == "--help") || arg == "?"){
|
||||||
|
cout << "Expected args:\n"
|
||||||
|
<< "\t --fps|-f \t the framerate, defaults to 20 Hz\n"
|
||||||
|
<< "\t --help|? \t this help\n"
|
||||||
|
<< "\t --width|-w \t the image width in pixel, defaults to 2000\n"
|
||||||
|
<< "\t --height|-h \t the image height in pixel, defaults to 2000\n"
|
||||||
|
<< "\t --cameras|-c \t the desired number of cameras to grab at the same time, defaults to 1\n"
|
||||||
|
<< "\t --framecount|-n \t the number of frames, defaults to 100\n"
|
||||||
|
<< "\t --quality|-q \t the qualtiy of the compression (0 < q <= 100), defaults to 50\n"
|
||||||
|
<< "\t --index|-i \t the camera index, ignored if cameras > 1, defaults to 0\n"
|
||||||
|
<< "\t --outfile|-o \t the output filename";
|
||||||
|
}
|
||||||
|
if ((arg == "--framecount" || arg == "-n") && i + 1 < argc) {
|
||||||
|
frameCount = std::atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
if ((arg == "--fps" || arg == "-f") && i + 1 < argc) {
|
||||||
|
framerate = std::atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
if ((arg == "--cameras" || arg == "-c") && i + 1 < argc) {
|
||||||
|
maxCameras = std::atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
if ((arg == "--width" || arg == "-w") && i + 1 < argc) {
|
||||||
|
cWidth = std::atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
if ((arg == "--height" || arg == "-h") && i + 1 < argc) {
|
||||||
|
cHeight = std::atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
if ((arg == "--index" || arg == "-i") && i + 1 < argc) {
|
||||||
|
camIndex = std::atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
if ((arg == "--quality" || arg == "-q") && i + 1 < argc) {
|
||||||
|
quality = (uint32_t)std::atoi(argv[++i]);
|
||||||
|
if (quality < 1)
|
||||||
|
quality = 1;
|
||||||
|
if (quality > 100)
|
||||||
|
quality = 100;
|
||||||
|
}
|
||||||
|
if ((arg == "--outfile" || arg == "-o") && i + 1 < argc) {
|
||||||
|
filename = Pylon::String_t(argv[++i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PylonInitialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if CVideoWriter is supported and all DLLs are available.
|
||||||
|
if (!CVideoWriter::IsSupported()) {
|
||||||
|
cout << "VideoWriter is not supported at the moment. Please install the pylon Supplementary Package for MPEG-4 which is available on the Basler website." << endl;
|
||||||
|
// Releases all pylon resources.
|
||||||
|
PylonTerminate();
|
||||||
|
// Return with error code 1.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTlFactory& tlFactory = CTlFactory::GetInstance();
|
||||||
|
DeviceInfoList_t devices;
|
||||||
|
if (tlFactory.EnumerateDevices( devices ) == 0) {
|
||||||
|
throw RUNTIME_EXCEPTION( "No camera present." );
|
||||||
|
}
|
||||||
|
|
||||||
|
CInstantCameraArray cameras( min( devices.size(), maxCameras ) );
|
||||||
|
for (size_t i = 0; i < cameras.GetSize(); ++i) {
|
||||||
|
if ( maxCameras == 1 && devices.size() > 1 && camIndex < devices.size() ){
|
||||||
|
cameras[i].Attach( tlFactory.CreateDevice( devices[camIndex] ) );
|
||||||
|
} else {
|
||||||
|
cameras[i].Attach( tlFactory.CreateDevice( devices[i] ) );
|
||||||
|
}
|
||||||
|
camCount += 1;
|
||||||
|
cameras[i].Open();
|
||||||
|
cout << "Using device " << cameras[i].GetDeviceInfo().GetModelName() << endl;
|
||||||
|
|
||||||
|
CIntegerParameter width( cameras[i].GetNodeMap(), "Width" );
|
||||||
|
CIntegerParameter height( cameras[i].GetNodeMap(), "Height" );
|
||||||
|
CEnumParameter pixelFormat( cameras[i].GetNodeMap(), "PixelFormat" );
|
||||||
|
CEnumParameter trigmode( cameras[i].GetNodeMap(), "TriggerMode");
|
||||||
|
trigmode.TrySetValue( "On" );
|
||||||
|
CEnumParameter trigsource( cameras[i].GetNodeMap(), "TriggerSource");
|
||||||
|
trigsource.TrySetValue( "Software" );
|
||||||
|
width.TrySetValue( cWidth, IntegerValueCorrection_Nearest );
|
||||||
|
height.TrySetValue( cHeight, IntegerValueCorrection_Nearest );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CIntegerParameter width( cameras[0].GetNodeMap(), "Width" );
|
||||||
|
CIntegerParameter height( cameras[0].GetNodeMap(), "Height" );
|
||||||
|
CEnumParameter pixelFormat( cameras[0].GetNodeMap(), "PixelFormat" );
|
||||||
|
CPixelTypeMapper pixelTypeMapper( &pixelFormat );
|
||||||
|
EPixelType pixelType = pixelTypeMapper.GetPylonPixelTypeFromNodeValue( pixelFormat.GetIntValue() );
|
||||||
|
|
||||||
|
CGrabResultPtr frames[camCount];
|
||||||
|
for (int i =0; i < camCount; i++) {
|
||||||
|
CGrabResultPtr ptrGrabResult;
|
||||||
|
frames[i] = ptrGrabResult;
|
||||||
|
}
|
||||||
|
Pylon::CPylonImage leftImage;
|
||||||
|
Pylon::CPylonImage rightImage;
|
||||||
|
Pylon::CPylonImage stitchedImage;
|
||||||
|
|
||||||
|
// Create a video writer object. Set parameters before opening the video writer.
|
||||||
|
CVideoWriter videoWriter;
|
||||||
|
videoWriter.SetParameter( (uint32_t) width.GetValue() * camCount,
|
||||||
|
(uint32_t) height.GetValue(),
|
||||||
|
pixelType,
|
||||||
|
framerate,
|
||||||
|
quality );
|
||||||
|
// Open the video writer.
|
||||||
|
videoWriter.Open( filename );
|
||||||
|
|
||||||
|
// Starts grabbing for all cameras starting with index 0. The grabbing
|
||||||
|
// is started for one camera after the other. That's why the images of all
|
||||||
|
// cameras are not taken at the same time.
|
||||||
|
// However, a hardware trigger setup can be used to cause all cameras to grab images synchronously.
|
||||||
|
// According to their default configuration, the cameras are
|
||||||
|
// set up for free-running continuous acquisition.
|
||||||
|
cameras.StartGrabbing();
|
||||||
|
int counter = 0;
|
||||||
|
auto before = high_resolution_clock::now();
|
||||||
|
auto done = high_resolution_clock::now();
|
||||||
|
auto total_duration = duration_cast<microseconds>(done - before);
|
||||||
|
int expected_usecs = (int)(1./framerate * 1000000);
|
||||||
|
|
||||||
|
while ( cameras.IsGrabbing() && !stop_request && counter < frameCount) {
|
||||||
|
if (counter > 0) {
|
||||||
|
long delay = total_duration.count() - expected_usecs;
|
||||||
|
if (delay > 0) {
|
||||||
|
cout << "frame: " << counter-1 << " took " << delay << " usecs too long to record!" << endl;
|
||||||
|
} else {
|
||||||
|
auto now = high_resolution_clock::now();
|
||||||
|
while (duration_cast<microseconds>( now - before).count() < expected_usecs ) {
|
||||||
|
now = high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
before = high_resolution_clock::now();
|
||||||
|
|
||||||
|
if ( camCount > 1 ) {
|
||||||
|
if ( cameras[0].WaitForFrameTriggerReady( 1000, Pylon::TimeoutHandling_ThrowException ) &&
|
||||||
|
cameras[1].WaitForFrameTriggerReady( 1000, Pylon::TimeoutHandling_ThrowException )) {
|
||||||
|
cameras[0].ExecuteSoftwareTrigger();
|
||||||
|
cameras[1].ExecuteSoftwareTrigger();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cameras[0].WaitForFrameTriggerReady( 1000, Pylon::TimeoutHandling_ThrowException );
|
||||||
|
cameras[0].ExecuteSoftwareTrigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( int i =0; i < camCount; ++i ) {
|
||||||
|
cameras[i].RetrieveResult( 5000, frames[i], TimeoutHandling_ThrowException );
|
||||||
|
}
|
||||||
|
// Image grabbed successfully?
|
||||||
|
bool success = true;
|
||||||
|
for ( int i =0; i < camCount; ++i ) {
|
||||||
|
success = success & frames[i]->GrabSucceeded();
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
if ( camCount > 1 ) {
|
||||||
|
leftImage.AttachGrabResultBuffer( frames[0] );
|
||||||
|
rightImage.AttachGrabResultBuffer( frames[1] );
|
||||||
|
StitchImage::StitchToRight(leftImage, rightImage, &stitchedImage, errorMessage);
|
||||||
|
videoWriter.Add( stitchedImage );
|
||||||
|
} else {
|
||||||
|
videoWriter.Add( frames[0] );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cout << "Error: " << std::hex << frames[0]->GetErrorCode() << std::dec << " " << frames[0]->GetErrorDescription() << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter += 1;
|
||||||
|
std::cout << "." << std::flush;
|
||||||
|
done = high_resolution_clock::now();
|
||||||
|
total_duration = duration_cast<microseconds>(done - before);
|
||||||
|
}
|
||||||
|
} catch (const GenericException& e) {
|
||||||
|
// Error handling
|
||||||
|
cerr << "An exception occurred." << endl << e.GetDescription() << endl;
|
||||||
|
exitCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Releases all pylon resources.
|
||||||
|
PylonTerminate();
|
||||||
|
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
605
stitchimage.h
Normal file
605
stitchimage.h
Normal file
@ -0,0 +1,605 @@
|
|||||||
|
// StitchImage.h
|
||||||
|
// Stitches multiple CPylonImage's into a single image, either vertically or horizontally.
|
||||||
|
// Also can make collages of images.
|
||||||
|
// Copyright (c) 2019 Matthew Breit - matt.breit@baslerweb.com or matt.breit@gmail.com
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http ://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef STITCHIMAGE_H
|
||||||
|
#define STITCHIMAGE_H
|
||||||
|
|
||||||
|
#ifndef LINUX_BUILD
|
||||||
|
#define WIN_BUILD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN_BUILD
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS // suppress fopen_s warnings for convinience
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Include Pylon libraries (if needed)
|
||||||
|
#include <pylon/PylonIncludes.h>
|
||||||
|
|
||||||
|
namespace StitchImage
|
||||||
|
{
|
||||||
|
int StitchToBottom(Pylon::CPylonImage &topImage, Pylon::CPylonImage &bottomImage, Pylon::CPylonImage *stitchedImage, std::string &errorMessage);
|
||||||
|
int StitchToRight(Pylon::CPylonImage &leftImage, Pylon::CPylonImage &rightImage, Pylon::CPylonImage *stitchedImage, std::string &errorMessage);
|
||||||
|
|
||||||
|
class CollageMaker
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Pylon::CPylonImage m_collageImage;
|
||||||
|
Pylon::CPylonImage m_tempImage;
|
||||||
|
Pylon::CPylonImage m_collageRow;
|
||||||
|
std::vector<Pylon::CPylonImage> m_collageRows;
|
||||||
|
int m_collageWidth = 0;
|
||||||
|
int m_collageHeight = 0;
|
||||||
|
int m_collageImagesCounter = 0;
|
||||||
|
bool m_collageComplete = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CollageMaker();
|
||||||
|
~CollageMaker();
|
||||||
|
|
||||||
|
int StitchToCollage(Pylon::CPylonImage &image, std::string &errorMessage);
|
||||||
|
int GetLatestCollage(Pylon::CPylonImage *collageImage, std::string &errorMessage);
|
||||||
|
int ResetCollage(std::string &errorMessage);
|
||||||
|
int GetWidth();
|
||||||
|
int GetHeight();
|
||||||
|
void SetWidth(int numImages);
|
||||||
|
void SetHeight(int numImages);
|
||||||
|
bool IsCollageComplete();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// *********************************************************************************************************
|
||||||
|
// DEFINITIONS
|
||||||
|
int StitchImage::StitchToBottom(Pylon::CPylonImage &topImage, Pylon::CPylonImage &bottomImage, Pylon::CPylonImage *stitchedImage, std::string &errorMessage)
|
||||||
|
{
|
||||||
|
errorMessage = "ERROR: ";
|
||||||
|
errorMessage.append(__FUNCTION__);
|
||||||
|
errorMessage.append("(): ");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Pylon::CPylonImage tempImage;
|
||||||
|
Pylon::EPixelType tempPixelType;
|
||||||
|
int tempWidth;
|
||||||
|
|
||||||
|
if (topImage.GetPixelType() == Pylon::EPixelType::PixelType_Undefined)
|
||||||
|
{
|
||||||
|
if (bottomImage.GetPixelType() == Pylon::EPixelType::PixelType_Undefined)
|
||||||
|
{
|
||||||
|
errorMessage.append("Both images have undefined pixel types!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempPixelType = bottomImage.GetPixelType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (topImage.GetPixelType() != bottomImage.GetPixelType())
|
||||||
|
{
|
||||||
|
errorMessage.append("Images must be same PixelType");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempPixelType = topImage.GetPixelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (topImage.GetWidth() == 0)
|
||||||
|
{
|
||||||
|
if (bottomImage.GetWidth() == 0)
|
||||||
|
{
|
||||||
|
errorMessage.append("Both Images have Width = 0!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempWidth = bottomImage.GetWidth();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (topImage.GetWidth() != bottomImage.GetWidth())
|
||||||
|
{
|
||||||
|
errorMessage.append("Images must be same Width!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempWidth = topImage.GetWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
int topImageHeight = topImage.GetHeight();
|
||||||
|
int bottomImageHeight = bottomImage.GetHeight();
|
||||||
|
size_t topImageSize = topImage.GetImageSize();
|
||||||
|
size_t bottomImageSize = bottomImage.GetImageSize();
|
||||||
|
int tempHeight = topImageHeight + bottomImageHeight;
|
||||||
|
|
||||||
|
tempImage.Reset(tempPixelType, tempWidth, tempHeight);
|
||||||
|
|
||||||
|
uint8_t *pTopImage = (uint8_t*)topImage.GetBuffer();
|
||||||
|
uint8_t *pBottomImage = (uint8_t*)bottomImage.GetBuffer();
|
||||||
|
uint8_t *pTempImage = (uint8_t*)tempImage.GetBuffer();
|
||||||
|
|
||||||
|
memcpy(&pTempImage[0], &pTopImage[0], topImageSize);
|
||||||
|
memcpy(&pTempImage[0 + topImageSize], &pBottomImage[0], bottomImageSize);
|
||||||
|
|
||||||
|
stitchedImage->CopyImage(tempImage);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (GenICam::GenericException &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.GetDescription());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append("UNKNOWN.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int StitchImage::StitchToRight(Pylon::CPylonImage &leftImage, Pylon::CPylonImage &rightImage, Pylon::CPylonImage *stitchedImage, std::string &errorMessage)
|
||||||
|
{
|
||||||
|
errorMessage = "ERROR: ";
|
||||||
|
errorMessage.append(__FUNCTION__);
|
||||||
|
errorMessage.append("(): ");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Pylon::CPylonImage tempImage;
|
||||||
|
Pylon::EPixelType tempPixelType;
|
||||||
|
int tempHeight;
|
||||||
|
|
||||||
|
if (Pylon::IsPacked(leftImage.GetPixelType()) == true || Pylon::IsPacked(rightImage.GetPixelType()) == true)
|
||||||
|
{
|
||||||
|
errorMessage.append("Packed pixel formats are not supported yet");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftImage.GetPixelType() == Pylon::EPixelType::PixelType_Undefined)
|
||||||
|
{
|
||||||
|
if (rightImage.GetPixelType() == Pylon::EPixelType::PixelType_Undefined)
|
||||||
|
{
|
||||||
|
errorMessage.append("Both images have undefined pixel types!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempPixelType = rightImage.GetPixelType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (leftImage.GetPixelType() != rightImage.GetPixelType())
|
||||||
|
{
|
||||||
|
errorMessage.append("Images must be same PixelType");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempPixelType = leftImage.GetPixelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (leftImage.GetHeight() == 0)
|
||||||
|
{
|
||||||
|
if (rightImage.GetHeight() == 0)
|
||||||
|
{
|
||||||
|
errorMessage.append("Both Images have Height = 0!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempHeight = rightImage.GetHeight();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (leftImage.GetHeight() != rightImage.GetHeight())
|
||||||
|
{
|
||||||
|
errorMessage.append("Images must be same Height!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tempHeight = leftImage.GetHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int BytesPerPixel = Pylon::BitPerPixel(tempPixelType) / 8;
|
||||||
|
int LeftImageWidth = leftImage.GetWidth();
|
||||||
|
int RightImageWidth = rightImage.GetWidth();
|
||||||
|
int tempWidth = LeftImageWidth + RightImageWidth;
|
||||||
|
|
||||||
|
tempImage.Reset(tempPixelType, tempWidth, tempHeight);
|
||||||
|
|
||||||
|
uint8_t *pLeftImage = (uint8_t*)leftImage.GetBuffer();
|
||||||
|
uint8_t *pRightImage = (uint8_t*)rightImage.GetBuffer();
|
||||||
|
uint8_t *pTempImage = (uint8_t*)tempImage.GetBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i < tempHeight; i++)
|
||||||
|
{
|
||||||
|
memcpy(&pTempImage[(tempWidth * i * BytesPerPixel)], &pLeftImage[LeftImageWidth * i * BytesPerPixel], LeftImageWidth * BytesPerPixel);
|
||||||
|
memcpy(&pTempImage[(tempWidth * i * BytesPerPixel) + (LeftImageWidth * BytesPerPixel)], &pRightImage[RightImageWidth * i * BytesPerPixel], RightImageWidth * BytesPerPixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
stitchedImage->CopyImage(tempImage);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (GenICam::GenericException &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.GetDescription());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append("UNKNOWN.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StitchImage::CollageMaker::CollageMaker()
|
||||||
|
{
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
StitchImage::CollageMaker::~CollageMaker()
|
||||||
|
{
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
int StitchImage::CollageMaker::StitchToCollage(Pylon::CPylonImage &image, std::string &errorMessage)
|
||||||
|
{
|
||||||
|
errorMessage = "ERROR: ";
|
||||||
|
errorMessage.append(__FUNCTION__);
|
||||||
|
errorMessage.append("(): ");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (StitchImage::StitchToRight(m_collageRow, image, &m_collageRow, errorMessage) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
m_collageComplete = false;
|
||||||
|
|
||||||
|
m_collageImagesCounter++;
|
||||||
|
|
||||||
|
if (m_collageImagesCounter % m_collageWidth == 0 && m_collageImagesCounter > 0)
|
||||||
|
{
|
||||||
|
m_collageRows.push_back(m_collageRow);
|
||||||
|
m_collageRow.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_collageImagesCounter % (m_collageWidth * m_collageHeight) == 0 && m_collageImagesCounter > 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_collageRows.size(); i++)
|
||||||
|
{
|
||||||
|
std::string errorMessage = "";
|
||||||
|
if (StitchImage::StitchToBottom(m_tempImage, m_collageRows[i], &m_tempImage, errorMessage) != 0)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
m_collageImage.CopyImage(m_tempImage);
|
||||||
|
m_tempImage.Release();
|
||||||
|
m_collageRow.Release();
|
||||||
|
m_collageRows.clear();
|
||||||
|
m_collageImagesCounter = 0;
|
||||||
|
m_collageComplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (GenICam::GenericException &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.GetDescription());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append("UNKNOWN.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int StitchImage::CollageMaker::GetLatestCollage(Pylon::CPylonImage *collageImage, std::string &errorMessage)
|
||||||
|
{
|
||||||
|
errorMessage = "ERROR: ";
|
||||||
|
errorMessage.append(__FUNCTION__);
|
||||||
|
errorMessage.append("(): ");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (m_collageImage.GetImageSize() == 0)
|
||||||
|
{
|
||||||
|
errorMessage.append("No Collage available yet");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
collageImage->CopyImage(m_collageImage);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (GenICam::GenericException &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.GetDescription());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append("UNKNOWN.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int StitchImage::CollageMaker::ResetCollage(std::string &errorMessage)
|
||||||
|
{
|
||||||
|
errorMessage = "ERROR: ";
|
||||||
|
errorMessage.append(__FUNCTION__);
|
||||||
|
errorMessage.append("(): ");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_tempImage.Release();
|
||||||
|
m_collageImage.Release();
|
||||||
|
m_collageRow.Release();
|
||||||
|
m_collageRows.clear();
|
||||||
|
m_collageImagesCounter = 0;
|
||||||
|
m_collageComplete = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (GenICam::GenericException &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.GetDescription());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append(e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
errorMessage.append("EXCEPTION: ");
|
||||||
|
errorMessage.append("UNKNOWN.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int StitchImage::CollageMaker::GetWidth()
|
||||||
|
{
|
||||||
|
return m_collageWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StitchImage::CollageMaker::GetHeight()
|
||||||
|
{
|
||||||
|
return m_collageHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StitchImage::CollageMaker::SetWidth(int numImages)
|
||||||
|
{
|
||||||
|
m_collageWidth = numImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StitchImage::CollageMaker::SetHeight(int numImages)
|
||||||
|
{
|
||||||
|
m_collageHeight = numImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StitchImage::CollageMaker::IsCollageComplete()
|
||||||
|
{
|
||||||
|
return m_collageComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
// *********************************************************************************************************
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// *********************************************************************************************************
|
||||||
|
// SAMPLE PROGRAM
|
||||||
|
/*
|
||||||
|
// Include files to use the PYLON API
|
||||||
|
#include <pylon/PylonIncludes.h>
|
||||||
|
#ifdef PYLON_WIN_BUILD
|
||||||
|
# include <pylon/PylonGUI.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "StitchImage.h"
|
||||||
|
|
||||||
|
// Namespace for using pylon objects.
|
||||||
|
using namespace Pylon;
|
||||||
|
|
||||||
|
#define USE_USB
|
||||||
|
|
||||||
|
#if defined( USE_1394 )
|
||||||
|
// Settings for using Basler IEEE 1394 cameras.
|
||||||
|
#include <pylon/1394/Basler1394InstantCamera.h>
|
||||||
|
typedef Pylon::CBasler1394InstantCamera Camera_t;
|
||||||
|
typedef Pylon::CBasler1394GrabResultPtr GrabResultPtr_t; // Or use Camera_t::GrabResultPtr_t
|
||||||
|
using namespace Basler_IIDC1394CameraParams;
|
||||||
|
#elif defined ( USE_GIGE )
|
||||||
|
// Settings for using Basler GigE cameras.
|
||||||
|
#include <pylon/gige/BaslerGigEInstantCamera.h>
|
||||||
|
typedef Pylon::CBaslerGigEInstantCamera Camera_t;
|
||||||
|
typedef Pylon::CBaslerGigEGrabResultPtr GrabResultPtr_t; // Or use Camera_t::GrabResultPtr_t
|
||||||
|
using namespace Basler_GigECameraParams;
|
||||||
|
#elif defined( USE_USB )
|
||||||
|
// Settings for using Basler USB cameras.
|
||||||
|
#include <pylon/usb/BaslerUsbInstantCamera.h>
|
||||||
|
typedef Pylon::CBaslerUsbInstantCamera Camera_t;
|
||||||
|
typedef Pylon::CBaslerUsbGrabResultPtr GrabResultPtr_t; // Or use Camera_t::GrabResultPtr_t
|
||||||
|
using namespace Basler_UsbCameraParams;
|
||||||
|
#else
|
||||||
|
#error Camera type is not specified. For example, define USE_GIGE for using GigE cameras.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Namespace for using cout.
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// Number of images to be grabbed.
|
||||||
|
static const uint32_t c_countOfImagesToGrab = 27;
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
// The exit code of the sample application.
|
||||||
|
int exitCode = 0;
|
||||||
|
|
||||||
|
// Before using any pylon methods, the pylon runtime must be initialized.
|
||||||
|
PylonInitialize();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Only look for cameras supported by Camera_t
|
||||||
|
CDeviceInfo info;
|
||||||
|
info.SetDeviceClass(Camera_t::DeviceClass());
|
||||||
|
info.SetSerialNumber("22479283");
|
||||||
|
|
||||||
|
// Create an instant camera object with the first found camera device that matches the specified device class.
|
||||||
|
Camera_t camera(CTlFactory::GetInstance().CreateFirstDevice(info));
|
||||||
|
|
||||||
|
// Print the model name of the camera.
|
||||||
|
cout << "Using device " << camera.GetDeviceInfo().GetModelName() << endl;
|
||||||
|
|
||||||
|
// Open the camera.
|
||||||
|
camera.Open();
|
||||||
|
|
||||||
|
camera.PixelFormat.SetValue(PixelFormat_Mono8);
|
||||||
|
camera.Width.SetValue(640);
|
||||||
|
camera.Height.SetValue(480);
|
||||||
|
camera.CenterX.SetValue(true);
|
||||||
|
camera.CenterY.SetValue(true);
|
||||||
|
camera.AcquisitionFrameRateEnable.SetValue(true);
|
||||||
|
camera.AcquisitionFrameRate.SetValue(1);
|
||||||
|
|
||||||
|
// This smart pointer will receive the grab result data.
|
||||||
|
GrabResultPtr_t ptrGrabResult;
|
||||||
|
|
||||||
|
// This pylon image will be the vertically stitched image
|
||||||
|
CPylonImage verticalStitchedImage;
|
||||||
|
|
||||||
|
// This pylon image will be the horizontally stitched image
|
||||||
|
CPylonImage horizontalStitchedImage;
|
||||||
|
|
||||||
|
// to make a collage
|
||||||
|
StitchImage::CollageMaker myCollageMaker;
|
||||||
|
myCollageMaker.SetWidth(3); // how many images wide do we want the collage to be
|
||||||
|
myCollageMaker.SetHeight(3); // how many images high do we want the collage to be
|
||||||
|
|
||||||
|
camera.StartGrabbing(c_countOfImagesToGrab);
|
||||||
|
|
||||||
|
// Camera.StopGrabbing() is called automatically by the RetrieveResult() method
|
||||||
|
// when c_countOfImagesToGrab images have been retrieved.
|
||||||
|
while (camera.IsGrabbing())
|
||||||
|
{
|
||||||
|
// Wait for an image and then retrieve it. A timeout of 5000 ms is used.
|
||||||
|
// RetrieveResult calls the image event handler's OnImageGrabbed method.
|
||||||
|
camera.RetrieveResult(5000, ptrGrabResult, TimeoutHandling_ThrowException);
|
||||||
|
|
||||||
|
if (ptrGrabResult->GrabSucceeded())
|
||||||
|
{
|
||||||
|
cout << "GrabSucceeded: " << ptrGrabResult->GrabSucceeded() << endl;
|
||||||
|
cout << "SizeX: " << ptrGrabResult->GetWidth() << endl;
|
||||||
|
cout << "SizeY: " << ptrGrabResult->GetHeight() << endl;
|
||||||
|
const uint8_t *pImageBuffer = (uint8_t *)ptrGrabResult->GetBuffer();
|
||||||
|
cout << "Gray value of first pixel: " << (uint32_t)pImageBuffer[0] << endl;
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
CPylonImage image;
|
||||||
|
image.AttachGrabResultBuffer(ptrGrabResult);
|
||||||
|
|
||||||
|
std::string errorMessage = "";
|
||||||
|
|
||||||
|
// make a tall strip of images (reusing the verticalStitchedImage as the Top image gives the effect of adding to the stitchedimage)
|
||||||
|
if (StitchImage::StitchToBottom(verticalStitchedImage, image, &verticalStitchedImage, errorMessage) == 0)
|
||||||
|
Pylon::DisplayImage(0, verticalStitchedImage);
|
||||||
|
else
|
||||||
|
cout << errorMessage << endl;
|
||||||
|
|
||||||
|
// make a wide strip of images (reusing the horizontalStitchedImage as the Left image gives the effect of adding to the stitchedimage)
|
||||||
|
if (StitchImage::StitchToRight(horizontalStitchedImage, image, &horizontalStitchedImage, errorMessage) == 0)
|
||||||
|
Pylon::DisplayImage(1, horizontalStitchedImage);
|
||||||
|
else
|
||||||
|
cout << errorMessage << endl;
|
||||||
|
|
||||||
|
// stitch to a collage (the images will be added to the collage in top-left to bottom-right order)
|
||||||
|
if (myCollageMaker.StitchToCollage(image, errorMessage) != 0)
|
||||||
|
cout << errorMessage << endl;
|
||||||
|
|
||||||
|
if (myCollageMaker.IsCollageComplete() == true)
|
||||||
|
{
|
||||||
|
CPylonImage myCollage;
|
||||||
|
if (myCollageMaker.GetCollageImage(&myCollage, errorMessage) == 0)
|
||||||
|
Pylon::DisplayImage(2, myCollage);
|
||||||
|
else
|
||||||
|
cout << errorMessage << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const GenericException &e)
|
||||||
|
{
|
||||||
|
// Error handling.
|
||||||
|
cerr << "An exception occurred." << endl
|
||||||
|
<< e.GetDescription() << endl;
|
||||||
|
exitCode = 1;
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
// Error handling.
|
||||||
|
cerr << "An exception occurred." << endl
|
||||||
|
<< e.what() << endl;
|
||||||
|
exitCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment the following two lines to disable waiting on exit.
|
||||||
|
cerr << endl << "Press Enter to exit." << endl;
|
||||||
|
while (cin.get() != '\n');
|
||||||
|
|
||||||
|
// Releases all pylon resources.
|
||||||
|
PylonTerminate();
|
||||||
|
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// *********************************************************************************************************
|
Loading…
Reference in New Issue
Block a user