MultiLightCtrl_ImageStitching.cpp

Multi-light control, image division, and image stitching of line-scan cameras.

The sample code shows how to set multi-light control parameters (the default is 2), divide the image and stitch the divided images in top-bottom direction to create a new image, and save the new image to local in BMP format.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include "MvCameraControl.h"
bool g_bExit = false;
#define IMAGE_NAME_LEN 256
// Wait for a pressed key
void PressEnterToExit(void)
{
int c;
while ( (c = getchar()) != '\n' && c != EOF );
fprintf( stderr, "\nPress enter to exit.\n");
while( getchar() != '\n');
g_bExit = true;
sleep(1);
}
// Get image pixel size
unsigned int GetPixelSize(enum MvGvspPixelType enPixelType);
// Check if it is Bayer format
bool IsBayerPixelFormat(MvGvspPixelType enType);
bool IsHBPixelFormat(MvGvspPixelType ePixelType)
{
switch (ePixelType)
{
return true;
default:
return false;
}
}
bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo)
{
if (NULL == pstMVDevInfo)
{
printf("The Pointer of pstMVDevInfo is NULL!\n");
return false;
}
if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
{
int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);
// Print current IP address, and user-defined name
printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4);
printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
}
else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
{
printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);
printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber);
printf("Device Number: %d\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.nDeviceNumber);
}
else if (pstMVDevInfo->nTLayerType == MV_GENTL_GIGE_DEVICE)
{
printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chSerialNumber);
printf("Model Name: %s\n\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName);
}
else if (pstMVDevInfo->nTLayerType == MV_GENTL_CAMERALINK_DEVICE)
{
printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stCMLInfo.chUserDefinedName);
printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stCMLInfo.chSerialNumber);
printf("Model Name: %s\n\n", pstMVDevInfo->SpecialInfo.stCMLInfo.chModelName);
}
else if (pstMVDevInfo->nTLayerType == MV_GENTL_CXP_DEVICE)
{
printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stCXPInfo.chUserDefinedName);
printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stCXPInfo.chSerialNumber);
printf("Model Name: %s\n\n", pstMVDevInfo->SpecialInfo.stCXPInfo.chModelName);
}
else if (pstMVDevInfo->nTLayerType == MV_GENTL_XOF_DEVICE)
{
printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stXoFInfo.chUserDefinedName);
printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stXoFInfo.chSerialNumber);
printf("Model Name: %s\n\n", pstMVDevInfo->SpecialInfo.stXoFInfo.chModelName);
}
else
{
printf("Not support.\n");
}
return true;
}
int main()
{
int nRet = MV_OK;
void* handle = NULL;
unsigned char* pReconstructBuffer = NULL;
unsigned char* pDstBuf = NULL;
unsigned int nExposureNum = 2; // Number of lights that support multi-light control function. The default is 2
do
{
// Initialize the SDK
nRet = MV_CC_Initialize();
if (MV_OK != nRet)
{
printf("Initialize SDK fail! nRet [0x%x]\n", nRet);
break;
}
// Enumerate devices
MV_CC_DEVICE_INFO_LIST stDeviceList;
memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
if (MV_OK != nRet)
{
printf("Enum Devices fail! nRet [0x%x]\n", nRet);
break;
}
if (stDeviceList.nDeviceNum > 0)
{
for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++)
{
printf("[device %d]:\n", i);
MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];
if (NULL == pDeviceInfo)
{
break;
}
PrintDeviceInfo(pDeviceInfo);
}
}
else
{
printf("Find No Devices!\n");
break;
}
printf("Please Input camera index(0-%d):", stDeviceList.nDeviceNum - 1);
unsigned int nIndex = 0;
scanf("%d", &nIndex);
if (nIndex >= stDeviceList.nDeviceNum)
{
printf("Input error!\n");
break;
}
// Select a device, and create a handle
nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);
if (MV_OK != nRet)
{
printf("Create Handle fail! nRet [0x%x]\n", nRet);
break;
}
// Turn on the device
nRet = MV_CC_OpenDevice(handle);
if (MV_OK != nRet)
{
printf("Open Device fail! nRet [0x%x]\n", nRet);
break;
}
// Get optimal packet size, and set it for GigE cameras
if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE)
{
int nPacketSize = MV_CC_GetOptimalPacketSize(handle);
if (nPacketSize > 0)
{
nRet = MV_CC_SetIntValue(handle, "GevSCPSPacketSize", nPacketSize);
if (nRet != MV_OK)
{
printf("Warning: Set Packet Size fail nRet [0x%x]!", nRet);
}
}
else
{
printf("Warning: Get Packet Size fail nRet [0x%x]!", nPacketSize);
}
}
// Get packet size
memset(&stParam, 0, sizeof(MVCC_INTVALUE_EX));
nRet = MV_CC_GetIntValueEx(handle, "PayloadSize", &stParam);
if (MV_OK != nRet)
{
printf("Get PayloadSize fail! nRet [0x%x]\n", nRet);
break;
}
int64_t nPayloadSize = stParam.nCurValue;
// Set camera exposure quantity (nExposureNum) to 2
int nRet = MV_CC_SetEnumValue(handle, "MultiLightControl", nExposureNum);
if (MV_OK != nRet)
{
printf("Set MultiLightControl fail,nRet:[%#x]\n", nRet);
}
else
{
printf("Set MultiLightControl to [%d]\n", nExposureNum);
}
// Get actual exposure quantity (nExposureNum)
nExposureNum = nExposureNum & 0xF;
// Set trigger mode to off
nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 0);
if (MV_OK != nRet)
{
printf("Set Trigger Mode fail! nRet [0x%x]\n", nRet);
break;
}
// Start grabbing image
nRet = MV_CC_StartGrabbing(handle);
if (MV_OK != nRet)
{
printf("Start Grabbing fail! nRet [0x%x]\n", nRet);
break;
}
MV_FRAME_OUT stImageInfo = { 0 };
MV_RECONSTRUCT_IMAGE_PARAM stImgReconstructionParam = { 0 };
uint64_t nReconstructBufferSize = 0;
MV_CC_HB_DECODE_PARAM stDecodeParam = { 0 };
nRet = MV_CC_GetImageBuffer(handle, &stImageInfo, 20000);// Larger image acquisition interval is needed for line scan cameras
if (nRet == MV_OK)
{
printf("Get Image Buffer: Width[%d], Height[%d], FrameNum[%d]\n",
stImageInfo.stFrameInfo.nExtendWidth, stImageInfo.stFrameInfo.nExtendHeight, stImageInfo.stFrameInfo.nFrameNum);
bool bpixel = IsHBPixelFormat(stImageInfo.stFrameInfo.enPixelType);
if (bpixel)
{
stDecodeParam.pSrcBuf = stImageInfo.pBufAddr;
stDecodeParam.nSrcLen = stImageInfo.stFrameInfo.nFrameLenEx;
if (pDstBuf == NULL)
{
pDstBuf = (unsigned char*)malloc(sizeof(unsigned char) * (nPayloadSize * 3));
if (NULL == pDstBuf)
{
printf("malloc pDstData fail !\n");
nRet = MV_E_RESOURCE;
break;
}
}
stDecodeParam.pDstBuf = pDstBuf;
stDecodeParam.nDstBufSize = nPayloadSize * 3;
nRet = MV_CC_HB_Decode(handle, &stDecodeParam);
if (nRet != MV_OK)
{
printf("Decode fail![0x%x]\n", nRet);
break;
}
else
{
printf("HB Decode success!\n");
}
stImgReconstructionParam.nWidth = stDecodeParam.nWidth;
stImgReconstructionParam.nHeight = stDecodeParam.nHeight;
stImgReconstructionParam.enPixelType = stDecodeParam.enDstPixelType;
stImgReconstructionParam.pSrcData = stDecodeParam.pDstBuf;
stImgReconstructionParam.nSrcDataLen = stDecodeParam.nDstBufLen;
}
else
{
// Assign value to image rebuilding structure
stImgReconstructionParam.nWidth = stImageInfo.stFrameInfo.nExtendWidth;
stImgReconstructionParam.nHeight = stImageInfo.stFrameInfo.nExtendHeight;
stImgReconstructionParam.enPixelType = stImageInfo.stFrameInfo.enPixelType;
stImgReconstructionParam.pSrcData = stImageInfo.pBufAddr;
stImgReconstructionParam.nSrcDataLen = stImageInfo.stFrameInfo.nFrameLenEx;
}
// Apply for buffer of divided images. The divided images are saved in pReconstructBuffer, which can be later be used for image saving
nReconstructBufferSize = stImgReconstructionParam.nSrcDataLen;
pReconstructBuffer = (unsigned char*)malloc(sizeof(unsigned char) * nReconstructBufferSize);
if (NULL == pReconstructBuffer)
{
printf("malloc pReconstructBuffer fail! nRet [0x%x]\n", nRet);
break;
}
// Calculate image size of each subimage
unsigned int nRowSize = stImgReconstructionParam.nWidth * GetPixelSize(stImgReconstructionParam.enPixelType);
unsigned int nSubImageHight = stImgReconstructionParam.nHeight / nExposureNum;
// To achieve interpolation, the height of image in Bayer format should be a a multiple of 2
if (IsBayerPixelFormat(stImgReconstructionParam.enPixelType))
{
nSubImageHight = nSubImageHight / 2 * 2;
}
uint64_t nSubImageSize = (uint64_t)nRowSize * nSubImageHight;
// Assign value to output data buffer to 2 (the light quantity)
// Perform top-down image stitching
for (int i = 0; i < nExposureNum; ++i)
{
stImgReconstructionParam.stDstBufList[i].pBuf = pReconstructBuffer + i * nSubImageSize; //pReconstructBuffer偏移
stImgReconstructionParam.stDstBufList[i].nBufSize = nSubImageSize;
}
stImgReconstructionParam.nExposureNum = nExposureNum;
stImgReconstructionParam.enReconstructMethod = MV_SPLIT_BY_LINE;
// Split and merge image
nRet = MV_CC_ReconstructImage(handle, &stImgReconstructionParam);
if (MV_OK != nRet)
{
printf("Reconstruct Image fail! nRet [0x%x]\n", nRet);
break;
}
// Save the originial image to BMP file
char chImageName[IMAGE_NAME_LEN] = { 0 };
MV_CC_IMAGE stImage;
memset(&stImage, 0, sizeof(MV_CC_IMAGE));
MV_CC_SAVE_IMAGE_PARAM stSaveImageParam;
memset(&stSaveImageParam, 0, sizeof(MV_CC_SAVE_IMAGE_PARAM));
stImage.enPixelType = stImgReconstructionParam.enPixelType;
stImage.nWidth = stImgReconstructionParam.nWidth;
stImage.nHeight = stImgReconstructionParam.nHeight;
stImage.nImageLen = stImgReconstructionParam.nSrcDataLen;
stImage.pImageBuf = stImgReconstructionParam.pSrcData;
stSaveImageParam.enImageType = MV_Image_Bmp;
stSaveImageParam.iMethodValue = 1;
sprintf(chImageName, "InPut_w%d_h%d_fn%03d.bmp", stImage.nWidth, stImage.nHeight, stImageInfo.stFrameInfo.nFrameNum);
nRet = MV_CC_SaveImageToFileEx2(handle, &stImage, &stSaveImageParam, chImageName);
if (nRet != MV_OK)
{
printf("raw image save to File fail nRet[%x]\n", nRet);
}
else
{
printf("raw image save to File success,save to %s\n", chImageName);
}
// Save the stitched image to BMP file
memset(&stImage, 0, sizeof(MV_CC_IMAGE));
stImage.enPixelType = stImgReconstructionParam.enPixelType;
stImage.nWidth = stImgReconstructionParam.nWidth;
for (int i = 0; i < nExposureNum; ++i)
{
stImage.nHeight += stImgReconstructionParam.stDstBufList[i].nHeight;
stImage.nImageLen += stImgReconstructionParam.stDstBufList[i].nBufLen;
}
stImage.pImageBuf = pReconstructBuffer;
sprintf(chImageName, "OutPut_w%d_h%d.bmp", stImage.nWidth, stImage.nHeight);
nRet = MV_CC_SaveImageToFileEx2(handle, &stImage, &stSaveImageParam, chImageName);
if (nRet != MV_OK)
{
printf("After reconstruction SaveImage To File fail nRet[%x]\n", nRet);
}
else
{
printf("After reconstruction SaveImage To File success, save to %s\n", chImageName);
}
nRet = MV_CC_FreeImageBuffer(handle, &stImageInfo);
if (nRet != MV_OK)
{
printf("Free Image Buffer fail! nRet [0x%x]\n", nRet);
}
if (NULL != pReconstructBuffer)
{
free(pReconstructBuffer);
pReconstructBuffer = NULL;
}
if (NULL != pDstBuf)
{
free(pDstBuf);
pDstBuf = NULL;
}
}
else
{
printf("Get Image fail! nRet [0x%x]\n", nRet);
}
// End grabbing image
nRet = MV_CC_StopGrabbing(handle);
if (MV_OK != nRet)
{
printf("Stop Grabbing fail! nRet [0x%x]\n", nRet);
break;
}
// Turn off the device
nRet = MV_CC_CloseDevice(handle);
if (MV_OK != nRet)
{
printf("ClosDevice fail! nRet [0x%x]\n", nRet);
break;
}
// Destroy the handle
nRet = MV_CC_DestroyHandle(handle);
if (MV_OK != nRet)
{
printf("Destroy Handle fail! nRet [0x%x]\n", nRet);
break;
}
handle = NULL;
} while (0);
if (MV_OK != nRet)
{
if (NULL != pDstBuf)
{
free(pDstBuf);
pDstBuf = NULL;
}
if (NULL != pReconstructBuffer)
{
free(pReconstructBuffer);
pReconstructBuffer = NULL;
}
}
if (handle != NULL)
{
handle = NULL;
}
// Deinitialize the SDK
PressEnterToExit();
return 0;
}
bool IsBayerPixelFormat(MvGvspPixelType enType)
{
switch (enType)
{
return true;
default:
return false;
}
}
unsigned int GetPixelSize(enum MvGvspPixelType enPixelType)
{
return (((enPixelType & 0x00ff0000) >> 16) / 8);
}