From e9b195674c5ce7dabc465b2e9d89643d31bceaf7 Mon Sep 17 00:00:00 2001 From: Jan Grewe Date: Tue, 12 Mar 2024 14:59:51 +0100 Subject: [PATCH] stolen stitchimage from MattsProjects --- stitchimage.h | 605 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 605 insertions(+) create mode 100644 stitchimage.h diff --git a/stitchimage.h b/stitchimage.h new file mode 100644 index 0000000..c7e7df4 --- /dev/null +++ b/stitchimage.h @@ -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 + +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 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 +#ifdef PYLON_WIN_BUILD +# include +#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 +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 +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 +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; +} +*/ +// ********************************************************************************************************* \ No newline at end of file