//------------------------------------------------------------- /** \file HalconQRCode.cpp \brief Sample to show how to acquire image continuously and QR Code Recognition \version 1.0.2012.9241 \date 2020.12.24 */ //------------------------------------------------------------- #include "halconcpp/HalconCpp.h" #include "GxIAPI.h" #include "DxImageProc.h" #include #include #include #include using namespace HalconCpp; #define ACQ_BUFFER_NUM 5 ///< Acquisition Buffer Qty. #define ACQ_TRANSFER_SIZE (64 * 1024) ///< Size of data transfer block #define ACQ_TRANSFER_NUMBER_URB 64 ///< Qty. of data transfer block #define FILE_NAME_LEN 50 ///< Save image file name length #define DETECT_STATUS_SUCCESS 0 ///< QRCode detected #define DETECT_STATUS_FAIL -1 ///< QRCode not detected #define PIXFMT_CVT_SUCCESS 0 ///< PixelFormatConvert success #define PIXFMT_CVT_FAIL -1 ///< PixelFormatConvert fail #define IMGBUF_TO_HOBJ_SUCCESS 0 ///< Image transform into Hobject success #define IMGBUF_TO_HOBJ_FAIL -1 ///< Image transform into Hobject fail //Show error message #define GX_VERIFY(emStatus) \ if (emStatus != GX_STATUS_SUCCESS) \ { \ GetErrorString(emStatus); \ return emStatus; \ } //Show error message, close device and lib #define GX_VERIFY_EXIT(emStatus) \ if (emStatus != GX_STATUS_SUCCESS) \ { \ GetErrorString(emStatus); \ GXCloseDevice(g_hDevice); \ g_hDevice = NULL; \ GXCloseLib(); \ printf("\n"); \ return emStatus; \ } GX_DEV_HANDLE g_hDevice = NULL; ///< Device handle bool g_bSavePPMImage = false; ///< SaveImage flag bool g_bColorFilter = false; ///< Color filter support flag int64_t g_i64ColorFilter = GX_COLOR_FILTER_NONE; ///< Color filter of device bool g_bAcquisitionFlag = false; ///< Thread running flag pthread_t g_nAcquisitonThreadID = 0; ///< Thread ID of Acquisition thread unsigned char* g_pRGBImageBuf = NULL; ///< Memory for RAW8toRGB24 unsigned char* g_pRaw8Image = NULL; ///< Memory for RAW16toRAW8 int64_t g_nPayloadSize = 0; ///< Payload size //Allocate the memory for pixel format transform void PreForAcquisition(); //Release the memory allocated void UnPreForAcquisition(); // Transform image buffer into halcon image data int ImgBuf2HObject(uint32_t, uint32_t, HObject); // Get QRCode Data int GetQRCodeData(const HObject, HTuple, HTuple, HTuple, HTuple); //Convert frame date to suitable pixel format int PixelFormatConvert(PGX_FRAME_BUFFER); //Save one frame to PPM image file void SavePPMFile(uint32_t, uint32_t); // Acquisition thread function void *ProcGetImage(void*); // Get description of error void GetErrorString(GX_STATUS); int main() { printf("\n"); printf("-------------------------------------------------------------\n"); printf("Sample to show how to acquire image continuously and QR Code Recognition.\n"); printf("version: 1.0.2012.9241\n"); printf("-------------------------------------------------------------\n"); printf("\n"); printf(""); printf("\n\n"); GX_STATUS emStatus = GX_STATUS_SUCCESS; uint32_t ui32DeviceNum = 0; //Initialize libary emStatus = GXInitLib(); if(emStatus != GX_STATUS_SUCCESS) { GetErrorString(emStatus); return emStatus; } //Get device enumerated number emStatus = GXUpdateDeviceList(&ui32DeviceNum, 1000); if(emStatus != GX_STATUS_SUCCESS) { GetErrorString(emStatus); GXCloseLib(); return emStatus; } //If no device found, app exit if(ui32DeviceNum <= 0) { printf("\n"); GXCloseLib(); return emStatus; } //Open first device enumerated emStatus = GXOpenDeviceByIndex(1, &g_hDevice); if(emStatus != GX_STATUS_SUCCESS) { GetErrorString(emStatus); GXCloseLib(); return emStatus; } //Get Device Info printf("***********************************************\n"); //Get libary version printf("\n", GXGetLibVersion()); size_t nSize = 0; //Get string length of Vendor name emStatus = GXGetStringLength(g_hDevice, GX_STRING_DEVICE_VENDOR_NAME, &nSize); GX_VERIFY_EXIT(emStatus); //Alloc memory for Vendor name char *pszVendorName = new char[nSize]; //Get Vendor name emStatus = GXGetString(g_hDevice, GX_STRING_DEVICE_VENDOR_NAME, pszVendorName, &nSize); if (emStatus != GX_STATUS_SUCCESS) { delete[] pszVendorName; pszVendorName = NULL; GX_VERIFY_EXIT(emStatus); } printf("\n", pszVendorName); //Release memory for Vendor name delete[] pszVendorName; pszVendorName = NULL; //Get string length of Model name emStatus = GXGetStringLength(g_hDevice, GX_STRING_DEVICE_MODEL_NAME, &nSize); GX_VERIFY_EXIT(emStatus); //Alloc memory for Model name char *pszModelName = new char[nSize]; //Get Model name emStatus = GXGetString(g_hDevice, GX_STRING_DEVICE_MODEL_NAME, pszModelName, &nSize); if (emStatus != GX_STATUS_SUCCESS) { delete[] pszModelName; pszModelName = NULL; GX_VERIFY_EXIT(emStatus); } printf("\n", pszModelName); //Release memory for Model name delete[] pszModelName; pszModelName = NULL; //Get string length of Serial number emStatus = GXGetStringLength(g_hDevice, GX_STRING_DEVICE_SERIAL_NUMBER, &nSize); GX_VERIFY_EXIT(emStatus); //Alloc memory for Serial number char *pszSerialNumber = new char[nSize]; //Get Serial Number emStatus = GXGetString(g_hDevice, GX_STRING_DEVICE_SERIAL_NUMBER, pszSerialNumber, &nSize); if (emStatus != GX_STATUS_SUCCESS) { delete[] pszSerialNumber; pszSerialNumber = NULL; GX_VERIFY_EXIT(emStatus); } printf("\n", pszSerialNumber); //Release memory for Serial number delete[] pszSerialNumber; pszSerialNumber = NULL; //Get string length of Device version emStatus = GXGetStringLength(g_hDevice, GX_STRING_DEVICE_VERSION, &nSize); GX_VERIFY_EXIT(emStatus); char *pszDeviceVersion = new char[nSize]; //Get Device Version emStatus = GXGetString(g_hDevice, GX_STRING_DEVICE_VERSION, pszDeviceVersion, &nSize); if (emStatus != GX_STATUS_SUCCESS) { delete[] pszDeviceVersion; pszDeviceVersion = NULL; GX_VERIFY_EXIT(emStatus); } printf("\n", pszDeviceVersion); //Release memory for Device version delete[] pszDeviceVersion; pszDeviceVersion = NULL; printf("***********************************************\n"); //Get the type of Bayer conversion. whether is a color camera. emStatus = GXIsImplemented(g_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &g_bColorFilter); GX_VERIFY_EXIT(emStatus); if (g_bColorFilter) { emStatus = GXGetEnum(g_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &g_i64ColorFilter); if (emStatus != GX_STATUS_SUCCESS) { GetErrorString(emStatus); return PIXFMT_CVT_FAIL; } } emStatus = GXGetInt(g_hDevice, GX_INT_PAYLOAD_SIZE, &g_nPayloadSize); GX_VERIFY(emStatus); printf("\n"); printf("Press [a] or [A] and then press [Enter] to start acquisition\n"); printf("Press [s] or [S] and then press [Enter] to save one ppm image\n"); printf("Press [x] or [X] and then press [Enter] to Exit the Program\n"); printf("\n"); int nStartKey = 0; bool bWaitStart = true; while (bWaitStart) { nStartKey = getchar(); switch(nStartKey) { //press 'a' and [Enter] to start acquisition //press 'x' and [Enter] to exit case 'a': case 'A': //Start to acquisition bWaitStart = false; break; case 'S': case 's': printf("\n"); break; case 'x': case 'X': //App exit GXCloseDevice(g_hDevice); g_hDevice = NULL; GXCloseLib(); printf("\n"); return 0; default: break; } } //Set acquisition mode emStatus = GXSetEnum(g_hDevice, GX_ENUM_ACQUISITION_MODE, GX_ACQ_MODE_CONTINUOUS); GX_VERIFY_EXIT(emStatus); //Set trigger mode emStatus = GXSetEnum(g_hDevice, GX_ENUM_TRIGGER_MODE, GX_TRIGGER_MODE_OFF); GX_VERIFY_EXIT(emStatus); //Set buffer quantity of acquisition queue uint64_t nBufferNum = ACQ_BUFFER_NUM; emStatus = GXSetAcqusitionBufferNumber(g_hDevice, nBufferNum); GX_VERIFY_EXIT(emStatus); bool bStreamTransferSize = false; emStatus = GXIsImplemented(g_hDevice, GX_DS_INT_STREAM_TRANSFER_SIZE, &bStreamTransferSize); GX_VERIFY_EXIT(emStatus); if(bStreamTransferSize) { //Set size of data transfer block emStatus = GXSetInt(g_hDevice, GX_DS_INT_STREAM_TRANSFER_SIZE, ACQ_TRANSFER_SIZE); GX_VERIFY_EXIT(emStatus); } bool bStreamTransferNumberUrb = false; emStatus = GXIsImplemented(g_hDevice, GX_DS_INT_STREAM_TRANSFER_NUMBER_URB, &bStreamTransferNumberUrb); GX_VERIFY_EXIT(emStatus); if(bStreamTransferNumberUrb) { //Set qty. of data transfer block emStatus = GXSetInt(g_hDevice, GX_DS_INT_STREAM_TRANSFER_NUMBER_URB, ACQ_TRANSFER_NUMBER_URB); GX_VERIFY_EXIT(emStatus); } //Prepare for Acquisition, alloc memory for image pixel format tansform PreForAcquisition(); //Device start acquisition emStatus = GXStreamOn(g_hDevice); if(emStatus != GX_STATUS_SUCCESS) { //Release the memory allocated UnPreForAcquisition(); GX_VERIFY_EXIT(emStatus); } //Start acquisition thread, if thread create failed, exit this app int nRet = pthread_create(&g_nAcquisitonThreadID, NULL, ProcGetImage, NULL); if(nRet != 0) { //Release the memory allocated UnPreForAcquisition(); GXCloseDevice(g_hDevice); g_hDevice = NULL; GXCloseLib(); printf("\n"); exit(nRet); } //Main loop bool bRun = true; while(bRun == true) { int nKey = getchar(); //press 's' and [Enter] to save image //press 'x' and [Enter] to exit switch(nKey) { //Save PPM Image case 'S': case 's': g_bSavePPMImage = true; break; //Exit app case 'X': case 'x': bRun = false; break; default: break; } } //Stop Acquisition thread g_bAcquisitionFlag = false; pthread_join(g_nAcquisitonThreadID, NULL); //Device stop acquisition emStatus = GXStreamOff(g_hDevice); if(emStatus != GX_STATUS_SUCCESS) { //Release the memory allocated UnPreForAcquisition(); GX_VERIFY_EXIT(emStatus); } //Release the resources and stop acquisition thread UnPreForAcquisition(); //Close device emStatus = GXCloseDevice(g_hDevice); if(emStatus != GX_STATUS_SUCCESS) { GetErrorString(emStatus); g_hDevice = NULL; GXCloseLib(); return emStatus; } //Release libary emStatus = GXCloseLib(); if(emStatus != GX_STATUS_SUCCESS) { GetErrorString(emStatus); return emStatus; } printf("\n"); return 0; } //------------------------------------------------- /** \brief Transform image buffer into halcon image data \param[in] ui32Width Image Width \param[in] ui32Height Image Height \param[out] pImg halcon: Image data \return int IMGBUF_TO_HOBJ_SUCCESS 0 IMGBUF_TO_HOBJ_FAIL -1 */ //------------------------------------------------- int ImgBuf2HObject(uint32_t ui32Width, uint32_t ui32Height, HObject *pImg) { unsigned char *pImageRed = NULL; unsigned char *pImageGreen = NULL; unsigned char *pImageBlue = NULL; pImageRed = (unsigned char*)malloc(ui32Width * ui32Height); if (pImageRed == NULL) { printf("Alloc red channel failed!\n"); return IMGBUF_TO_HOBJ_FAIL; } pImageGreen = (unsigned char*)malloc(ui32Width * ui32Height); if (pImageGreen == NULL) { free(pImageRed); pImageRed = NULL; printf("Alloc green channel failed!\n"); return IMGBUF_TO_HOBJ_FAIL; } pImageBlue = (unsigned char*)malloc(ui32Width * ui32Height); if (pImageBlue == NULL) { free(pImageRed); pImageRed = NULL; free(pImageGreen); pImageGreen = NULL; printf("Alloc blue channel failed!\n"); return IMGBUF_TO_HOBJ_FAIL; } const unsigned char* pRGBImageTmp = g_pRGBImageBuf; for (int row = 0; row < ui32Height; row++) { for (int col = 0; col < ui32Width; col++) { pImageRed [row * ui32Width + col] = pRGBImageTmp[0]; pImageGreen[row * ui32Width + col] = pRGBImageTmp[1]; pImageBlue [row * ui32Width + col] = pRGBImageTmp[2]; pRGBImageTmp += 3; } } // Create a three-channel image from three pointers on the pixels with storage management. GenImage3Extern(pImg, "byte", (Hlong)ui32Width, (Hlong)ui32Height, (Hlong)pImageRed, (Hlong)pImageGreen, (Hlong)pImageBlue, (Hlong)&free); return IMGBUF_TO_HOBJ_SUCCESS; } //------------------------------------------------- /** \brief Get QR Code Data \param[in] objImage halcon: Image data \param[out] pobjDecodedDataStrings halcon: Data string from QR Code \param[out] pobjSymbolXLDs halcon: QR Code Area \param[out] pobjTime halcon: Recognize time \return int DETECT_STATUS_SUCCESS QRCode Detected DETECT_STATUS_FAIL QRCode Not Detected */ //------------------------------------------------- int GetQRCodeData(const HImage *objImage, HTuple *pobjDecodedDataStrings, HObject *pobjSymbolXLDs, HTuple *pobjTime) { // Local control variables HTuple objDataCodeHandle; HTuple objResultHandles; HTuple objSecondsBegin; HTuple objSecondsEnd; // Create a model of a 2D data code class. CreateDataCode2dModel("QR Code", HTuple(), HTuple(), &objDataCodeHandle); // Start Time. CountSeconds(&objSecondsBegin); // Detect and read 2D data code symbols in an image or train the 2D data code model. FindDataCode2d(*objImage, pobjSymbolXLDs, objDataCodeHandle, HTuple(), HTuple(), &objResultHandles, pobjDecodedDataStrings); // Passed Time. CountSeconds(&objSecondsEnd); *pobjTime = (objSecondsEnd - objSecondsBegin) * 1000; // Delete a 2D data code model and free the allocated memory. ClearDataCode2dModel(objDataCodeHandle); if (objResultHandles == 0) { return DETECT_STATUS_SUCCESS; } else { return DETECT_STATUS_FAIL; } } //------------------------------------------------- /** \brief Convert frame date to suitable pixel format \param pFrameBuffer[in] FrameData from camera \return void */ //------------------------------------------------- int PixelFormatConvert(PGX_FRAME_BUFFER pFrameBuffer) { VxInt32 emDXStatus = DX_OK; // Convert RAW8 or RAW16 image to RGB24 image switch (pFrameBuffer->nPixelFormat) { case GX_PIXEL_FORMAT_MONO8: case GX_PIXEL_FORMAT_BAYER_GR8: case GX_PIXEL_FORMAT_BAYER_RG8: case GX_PIXEL_FORMAT_BAYER_GB8: case GX_PIXEL_FORMAT_BAYER_BG8: { // Convert to the RGB image emDXStatus = DxRaw8toRGB24((unsigned char*)pFrameBuffer->pImgBuf, g_pRGBImageBuf, pFrameBuffer->nWidth, pFrameBuffer->nHeight, RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(g_i64ColorFilter), false); if (emDXStatus != DX_OK) { printf("DxRaw8toRGB24 Failed, Error Code: %d\n", emDXStatus); return PIXFMT_CVT_FAIL; } break; } case GX_PIXEL_FORMAT_MONO10: case GX_PIXEL_FORMAT_MONO12: case GX_PIXEL_FORMAT_BAYER_GR10: case GX_PIXEL_FORMAT_BAYER_RG10: case GX_PIXEL_FORMAT_BAYER_GB10: case GX_PIXEL_FORMAT_BAYER_BG10: case GX_PIXEL_FORMAT_BAYER_GR12: case GX_PIXEL_FORMAT_BAYER_RG12: case GX_PIXEL_FORMAT_BAYER_GB12: case GX_PIXEL_FORMAT_BAYER_BG12: { // Convert to the Raw8 image emDXStatus = DxRaw16toRaw8((unsigned char*)pFrameBuffer->pImgBuf, g_pRaw8Image, pFrameBuffer->nWidth, pFrameBuffer->nHeight, DX_BIT_2_9); if (emDXStatus != DX_OK) { printf("DxRaw16toRaw8 Failed, Error Code: %d\n", emDXStatus); return PIXFMT_CVT_FAIL; } // Convert to the RGB24 image emDXStatus = DxRaw8toRGB24(g_pRaw8Image, g_pRGBImageBuf, pFrameBuffer->nWidth, pFrameBuffer->nHeight, RAW2RGB_NEIGHBOUR, DX_PIXEL_COLOR_FILTER(g_i64ColorFilter), false); if (emDXStatus != DX_OK) { printf("DxRaw8toRGB24 Failed, Error Code: %d\n", emDXStatus); return PIXFMT_CVT_FAIL; } break; } default: { printf("Error : PixelFormat of this camera is not supported\n"); return PIXFMT_CVT_FAIL; } } return PIXFMT_CVT_SUCCESS; } //------------------------------------------------- /** \brief Save PPM image \param ui32Width[in] image width \param ui32Height[in] image height \return void */ //------------------------------------------------- void SavePPMFile(uint32_t ui32Width, uint32_t ui32Height) { char szName[FILE_NAME_LEN] = {0}; static int nRawFileIndex = 0; //Save color image if(g_pRGBImageBuf != NULL) { FILE* phImageFile = NULL; snprintf(szName, FILE_NAME_LEN, "Frame_%d.ppm", nRawFileIndex++); phImageFile = fopen(szName,"wb"); if (phImageFile == NULL) { printf("Create or Open %s failed!\n", szName); return; } fprintf(phImageFile, "P6\n%u %u 255\n", ui32Width, ui32Height); fwrite(g_pRGBImageBuf, 1, g_nPayloadSize * 3, phImageFile); fclose(phImageFile); phImageFile = NULL; printf("Save %s succeed\n", szName); } else { printf("Save %s failed!\n", szName); } return; } //------------------------------------------------- /** \brief Allocate the memory for pixel format transform \return void */ //------------------------------------------------- void PreForAcquisition() { //Alloc memory for color image pixel format transform g_pRGBImageBuf = new unsigned char[g_nPayloadSize * 3]; g_pRaw8Image = new unsigned char[g_nPayloadSize]; return; } //------------------------------------------------- /** \brief Release the memory allocated \return void */ //------------------------------------------------- void UnPreForAcquisition() { //Release resources if (g_pRaw8Image != NULL) { delete[] g_pRaw8Image; g_pRaw8Image = NULL; } if (g_pRGBImageBuf != NULL) { delete[] g_pRGBImageBuf; g_pRGBImageBuf = NULL; } return; } //------------------------------------------------- /** \brief Acquisition thread function \param pParam[in] thread param, not used in this app \return void* */ //------------------------------------------------- void *ProcGetImage(void* pParam) { GX_STATUS emStatus = GX_STATUS_SUCCESS; int nStatusBuf2Hobj = IMGBUF_TO_HOBJ_SUCCESS; // Thread running flag setup g_bAcquisitionFlag = true; PGX_FRAME_BUFFER pFrameBuffer = NULL; // Halcon Window Size const uint32_t ui32X = 16; const uint32_t ui32Y = 16; const uint32_t ui32WindowWidth = 960; const uint32_t ui32WindowHeight = 600; // Setup Window, Halcon will throw a exception when license missing, exit when exception catched. HWindow objWindow; try { objWindow = HWindow(ui32X, ui32Y, ui32WindowWidth, ui32WindowHeight, 0, "visible", ""); } catch(...) { UnPreForAcquisition(); printf("\n"); exit(0); } int64_t nWidth = 0; int64_t nHeight = 0; emStatus = GXGetInt(g_hDevice, GX_INT_WIDTH, &nWidth); if (emStatus != GX_STATUS_SUCCESS) { printf("Get camera info failed, please restart this app!\n"); return 0; } emStatus = GXGetInt(g_hDevice, GX_INT_HEIGHT, &nHeight); if (emStatus != GX_STATUS_SUCCESS) { printf("Get camera info failed, please restart this app!\n"); return 0; } HTuple objDecodedDataStrings; HTuple objTime; HImage objImage; HObject objSymbolXLDs; int nStatusDetect = DETECT_STATUS_SUCCESS; // Set Window Size objWindow.SetPart(0, 0, nHeight - 1, nWidth - 1); objWindow.SetLineWidth(3); while(g_bAcquisitionFlag) { // Get a frame from Queue emStatus = GXDQBuf(g_hDevice, &pFrameBuffer, 1000); if(emStatus != GX_STATUS_SUCCESS) { if (emStatus == GX_STATUS_TIMEOUT) { continue; } else { GetErrorString(emStatus); break; } } if(pFrameBuffer->nStatus != GX_FRAME_STATUS_SUCCESS) { printf("\n", pFrameBuffer->nStatus); } else { emStatus = PixelFormatConvert(pFrameBuffer); if(emStatus != GX_STATUS_SUCCESS) { GXQBuf(g_hDevice, pFrameBuffer); break; } if (g_bSavePPMImage) { SavePPMFile(pFrameBuffer->nWidth, pFrameBuffer->nHeight); g_bSavePPMImage = false; } // Transform Image buffer to HImage nStatusBuf2Hobj = ImgBuf2HObject((uint32_t)nWidth, (uint32_t)nHeight, &objImage); if (nStatusBuf2Hobj != IMGBUF_TO_HOBJ_SUCCESS) { GXQBuf(g_hDevice, pFrameBuffer); printf("Transform Image buffer to HImage failed!\n"); break; } // Display image objWindow.DispObj(objImage); // Get QR Code Data and QR Code Area nStatusDetect = GetQRCodeData(&objImage, &objDecodedDataStrings, &objSymbolXLDs, &objTime); if (nStatusDetect == DETECT_STATUS_SUCCESS) { // Print String to console printf("QR Code Result String:%s Using %.3fms\n", objDecodedDataStrings.S().Text(), objTime.D()); // Display QR Code Area objWindow.DispObj(objSymbolXLDs); } } // Put the frame back to Queue emStatus = GXQBuf(g_hDevice, pFrameBuffer); if(emStatus != GX_STATUS_SUCCESS) { GetErrorString(emStatus); break; } } printf("\n"); return 0; } //---------------------------------------------------------------------------------- /** \brief Get description of input error code \param[in] emErrorStatus error code \return void */ //---------------------------------------------------------------------------------- void GetErrorString(GX_STATUS emErrorStatus) { char *error_info = NULL; size_t size = 0; GX_STATUS emStatus = GX_STATUS_SUCCESS; // Get length of error description emStatus = GXGetLastError(&emErrorStatus, NULL, &size); if(emStatus != GX_STATUS_SUCCESS) { printf("\n"); return; } // Alloc error resources error_info = new char[size]; if (error_info == NULL) { printf("\n"); return ; } // Get error description emStatus = GXGetLastError(&emErrorStatus, error_info, &size); if (emStatus != GX_STATUS_SUCCESS) { printf("\n"); } else { printf("%s\n", error_info); } // Realease error resources if (error_info != NULL) { delete []error_info; error_info = NULL; } }