Hikvision 産業用カメラレンズシェーディング補正 LSC
LSC の原理の紹介、一部の参照内容: ISP レンズ シェーディング補正 (LSC)
序文
- レンズシェーディング補正(レンズシェーディング補正)とは、レンズの光学特性やレンズの光の屈折不均一によるレンズ周囲の影を解決することです。
- レンズの光学特性により、センサー撮像エリアの端部で受光する光量が中央部に比べて小さくなり、中央部と四隅で明るさにばらつきが生じます。レンズ自体は凸レンズですが、凸レンズの原理により、中心部の光感度が周辺部よりも高くなければなりません。図に示すように、
実際の結像効果は図に示すように、
中央領域が明るく、四隅が黒になります。
Haikang 産業用カメラ SDK LSC アルゴリズム修正インターフェイス
SDKのダウンロードと入手
Haikang Machine Vision SDKの概要、ダウンロード リンク: Machine Vision ダウンロード センター
、MVS サンプル プログラム デモをダウンロードしてインストールするだけです。「C:\Program Files (x86)\MVS\Development\Samples\VC\VS\SimpleSamples* LensShadingCorrection *」を参照してください。 」
コード フローの概要
LSC 補正は主にキャリブレーション ファイルの生成と LSC 補正の使用に分かれます
LSCキャリブレーションファイルの生成
中心となるアイデアは、キャリブレーション インターフェイスを呼び出して、ストリーミング インターフェイスでキャリブレーション ファイルを生成するか、キャリブレーションされる画像を生成することです。次のコードが呼び出され、コールバック関数で MV_CC_LSCCalib インターフェイスを呼び出して LSCCalib.bin を生成し
ます。キャリブレーションファイル
void __stdcall ImageCallBackEx(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
printf("Get One Frame: Width[%d], Height[%d], nFrameNum[%d]\n", pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nFrameNum);
int nRet = MV_OK;
//判断是否需要标定
if (true == g_IsNeedCalib)
{
// LSC标定
MV_CC_LSC_CALIB_PARAM stLSCCalib = {
0};
stLSCCalib.nWidth = pFrameInfo->nWidth;
stLSCCalib.nHeight = pFrameInfo->nHeight;
stLSCCalib.enPixelType = pFrameInfo->enPixelType;
stLSCCalib.pSrcBuf = pData;
stLSCCalib.nSrcBufLen = pFrameInfo->nFrameLen;
if (g_pCalibBuf == NULL || g_nCalibBufSize < (pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short)))
{
if (g_pCalibBuf)
{
free(g_pCalibBuf);
g_pCalibBuf = NULL;
g_nCalibBufSize = 0;
}
g_pCalibBuf = (unsigned char *)malloc(pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short));
if (g_pCalibBuf == NULL)
{
printf("malloc pCalibBuf fail !\n");
return;
}
g_nCalibBufSize = pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short);
}
stLSCCalib.pCalibBuf = g_pCalibBuf;
stLSCCalib.nCalibBufSize = g_nCalibBufSize;
stLSCCalib.nSecNumW = 689;
stLSCCalib.nSecNumH = 249;
stLSCCalib.nPadCoef = 2;
stLSCCalib.nCalibMethod = 2;
stLSCCalib.nTargetGray = 100;
//同一个相机,在场景、算法参数和图像参数都不变情况下,理论上只需要进行一次标定,可将标定表保存下来。
//不同相机图片标定出来的标定表不可复用,当场景改变或算法参数改变或图像参数改变时,需要重新标定。
nRet = MV_CC_LSCCalib(pUser, &stLSCCalib);
if (MV_OK != nRet)
{
printf("LSC Calib fail! nRet [0x%x]\n", nRet);
return;
}
//保存标定表到本地
FILE* fp_out = fopen("./LSCCalib.bin", "wb");
if (NULL == fp_out)
{
return ;
}
fwrite(stLSCCalib.pCalibBuf, 1, stLSCCalib.nCalibBufLen, fp_out);
fclose(fp_out);
g_IsNeedCalib = false;
}
}
MV_CC_LSCCalib インターフェイスでは、アルゴリズム パラメーターを渡す必要があります。この構造体の構成を見てみましょう MV_CC_LSC_CALIB_PARAM 従来の画像の幅、高
さ、ピクセル形式のパラメーターに加えて、いくつかのアルゴリズム パラメーターについて次のように説明します。
パラメータ | 説明 | 推奨値 |
---|---|---|
nSecNumW | 幅スコア、nSecNumW が大きいほど、修正後の一貫性は向上しますが、値が大きいほど、一部の欠陥検出欠陥が生じます (nSecNumW<widthmax)。 | 画像の最大幅を 4 で割った値 |
nSecNumH | 高さスコア、nSecNumH が大きいほど、修正後の一貫性は向上しますが、値が大きいほど、一部の欠陥検出欠陥が歪みます。nSecNumH<hightmax | 画像の最大高さを 4 で割った値 |
nPadCoef | エッジフィルファクター、範囲 1 ~ 5 | 2 |
nCalibMethod | キャリブレーション方法[0-2]、3つのキャリブレーション方法が提供されています1:中心が明るさの基準であり、画像の中心領域のグレー値が補正の基準となります、2:最も明るい領域、3: : ターゲットの明るさ、ピクセル形式が 8 ビットの場合、nTargetGray 値の範囲は [0-255] に設定できます。ピクセル形式が 10 ビットの場合、nTargetGray の値の範囲は [0-1023] に設定できます。形式は 12 ビットで、nTargetGray 値の範囲は [0-4095] に設定できます。 | 0 |
nTargetGray | nCalibMethodの値が2の場合に有効 | nCalibMethod が 2 の場合、画像中央領域の平均輝度 |
typedef struct _MV_CC_LSC_CALIB_PARAM_T_
{
unsigned int nWidth; ///< [IN] \~chinese 图像宽度[16,65535] \~english Image Width
unsigned int nHeight; ///< [IN] \~chinese 图像高度[16-65535] \~english Image Height
enum MvGvspPixelType enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format
unsigned char* pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer
unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length
unsigned char* pCalibBuf; ///< [OUT] \~chinese 输出标定表缓存 \~english Output calib buffer
unsigned int nCalibBufSize; ///< [IN] \~chinese 提供的标定表缓冲大小(nWidth*nHeight*sizeof(unsigned short)) \~english Provided output buffer size
unsigned int nCalibBufLen; ///< [OUT] \~chinese 输出标定表缓存长度 \~english Output calib buffer length
unsigned int nSecNumW; ///< [IN] \~chinese 宽度分块数 \~english Width Sec num
unsigned int nSecNumH; ///< [IN] \~chinese 高度分块数 \~english Height Sec num
unsigned int nPadCoef; ///< [IN] \~chinese 边缘填充系数[1,5] \~english Pad Coef[1,5]
unsigned int nCalibMethod; ///< [IN] \~chinese 标定方式(0-中心为基准;1-最亮区域为基准;2-目标亮度为基准) \~english Calib method
unsigned int nTargetGray; ///< [IN] \~chinese 目标亮度(标定方式为2时有效) \~english Target Gray
///< \~chinese 8位,范围:[0,255] \~english 8bit,range:[0,255]
///< \~chinese 10位,范围:[0,1023] \~english 10bit,range:[0,1023]
///< \~chinese 12位,范围:[0,4095] \~english 12bit,range:[0,4095]
unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved
}MV_CC_LSC_CALIB_PARAM;
キャリブレーション プロセス中に特定のシーンが必要な場合は、次の手順に従います。
1. カメラを均一なシーン (均一な光源、白い紙、ホワイト ボードなど) に向け、露出やゲインなどを調整します。画像のグレースケールが 120 ~ 160 になるようにします (レンズの絞りは実際の使用シーンと一致するようにします)
2. キャリブレーション プログラムを実行してキャリブレーション ファイルを生成します。LensShadingCorrection サンプル デモを直接実行すると、現在のキャリブレーション ファイルが生成されます。 3. 実際のオブジェクト
を撮影し、補正プログラムを実行し、効果を観察します。LensShadingCorrection を直接実行できます。デモ観察の例
キャリブレーション補正の効果が良くない場合は、キャリブレーション シーンを再調整して、
同様に、複数の光源切り替えシーンが発生した場合は、動的切り替え補正用に異なるキャリブレーション ファイルを生成できます。
LSC補正
キャリブレーション ファイルを取得した後の補正プロセスは比較的簡単です。画像を取得した後、MV_CC_LSCCorrect インターフェイスを呼び出して完了します (すべての画像を 1 つずつ呼び出して補正する必要があります)。
void __stdcall ImageCallBackEx(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
printf("Get One Frame: Width[%d], Height[%d], nFrameNum[%d]\n", pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nFrameNum);
int nRet = MV_OK;
// LSC校正
if (g_pDstData == NULL || g_nDstDataSize < pFrameInfo->nFrameLen)
{
if (g_pDstData)
{
free(g_pDstData);
g_pDstData = NULL;
g_nDstDataSize = 0;
}
g_pDstData = (unsigned char *)malloc(pFrameInfo->nFrameLen);
if (g_pDstData == NULL)
{
printf("malloc pDstData fail !\n");
return;
}
g_nDstDataSize = pFrameInfo->nFrameLen;
}
MV_CC_LSC_CORRECT_PARAM stLSCCorrectParam = {
0};
stLSCCorrectParam.nWidth = pFrameInfo->nWidth;
stLSCCorrectParam.nHeight = pFrameInfo->nHeight;
stLSCCorrectParam.enPixelType = pFrameInfo->enPixelType;
stLSCCorrectParam.pSrcBuf = pData;
stLSCCorrectParam.nSrcBufLen = pFrameInfo->nFrameLen;
stLSCCorrectParam.pDstBuf = g_pDstData;
stLSCCorrectParam.nDstBufSize = g_nDstDataSize;
stLSCCorrectParam.pCalibBuf = g_pCalibBuf;
stLSCCorrectParam.nCalibBufLen = g_nCalibBufSize;
nRet = MV_CC_LSCCorrect(pUser, &stLSCCorrectParam);
if (MV_OK != nRet)
{
printf("LSC Correct fail! nRet [0x%x]\n", nRet);
return;
}
//矫正完成,其他图像处理
}
時間のかかる修正テスト
テスト 1: 異なる解像度の画像、異なるバージョンのアルゴリズム、時間がかかる状況
解決 | x64 | ×32 |
---|---|---|
2448*2048 | 4.3ミリ秒 | 5.6ミリ秒 |
3840*2748 | 8.6ミリ秒 | 11.89ミリ秒 |
5472*3648 | 16.45ミリ秒 | 22.14ミリ秒 |
- アルゴリズムの所要時間は画像の解像度に依存します。解像度が大きくなるほど、所要時間も長くなります。
テスト 2: さまざまなアルゴリズム パラメーター、補正モジュールの所要時間
解決 | nCalibMethod | 時間がかかる | ブロック数 | 時間がかかる | エッジフィルファクター | 時間がかかる |
---|---|---|---|---|---|---|
2448*2048 | 0: 中心基準 | 4.25ミリ秒 | 16*16 | 4.28ミリ秒 | 1 | 4.26ミリ秒 |
2448*2048 | 1: 最も明るいベンチマーク | 4.25ミリ秒 | 600*400 | 4.30ミリ秒 | 3 | 4.20ミリ秒 |
2448*2048 | 2: 目標ベンチマーク | 4.28ミリ秒 | 2448*2048 | 4.25ミリ秒 | 5 | 4.26ミリ秒 |
画像効果の比較
テストパラメータ:
パラメータ | 価値 |
---|---|
解決 | 2448*2048 |
nSecNumW | 2448 |
nSecNumH | 2048年 |
nPadCoef | 2 |
nCalibMethod | 0 |
LensShadingCorrection 完全サンプルプログラム
#include <stdio.h>
#include <Windows.h>
#include <conio.h>
#include <io.h>
#include "MvCameraControl.h"
// ch:等待按键输入 | en:Wait for key press
void WaitForKeyPress(void)
{
while(!_kbhit())
{
Sleep(10);
}
_getch();
}
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);
// ch:打印当前相机ip和用户自定义名字 | en:print current ip 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
{
printf("Not support.\n");
}
return true;
}
unsigned char * g_pDstData = NULL;
unsigned int g_nDstDataSize = 0;
unsigned char * g_pCalibBuf = NULL;
unsigned int g_nCalibBufSize = 0;
bool g_IsNeedCalib = true;
void __stdcall ImageCallBackEx(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
printf("Get One Frame: Width[%d], Height[%d], nFrameNum[%d]\n", pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nFrameNum);
int nRet = MV_OK;
//判断是否需要标定
if (true == g_IsNeedCalib)
{
// LSC标定
MV_CC_LSC_CALIB_PARAM stLSCCalib = {
0};
stLSCCalib.nWidth = pFrameInfo->nWidth;
stLSCCalib.nHeight = pFrameInfo->nHeight;
stLSCCalib.enPixelType = pFrameInfo->enPixelType;
stLSCCalib.pSrcBuf = pData;
stLSCCalib.nSrcBufLen = pFrameInfo->nFrameLen;
if (g_pCalibBuf == NULL || g_nCalibBufSize < (pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short)))
{
if (g_pCalibBuf)
{
free(g_pCalibBuf);
g_pCalibBuf = NULL;
g_nCalibBufSize = 0;
}
g_pCalibBuf = (unsigned char *)malloc(pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short));
if (g_pCalibBuf == NULL)
{
printf("malloc pCalibBuf fail !\n");
return;
}
g_nCalibBufSize = pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short);
}
stLSCCalib.pCalibBuf = g_pCalibBuf;
stLSCCalib.nCalibBufSize = g_nCalibBufSize;
stLSCCalib.nSecNumW = 689;
stLSCCalib.nSecNumH = 249;
stLSCCalib.nPadCoef = 2;
stLSCCalib.nCalibMethod = 2;
stLSCCalib.nTargetGray = 100;
//同一个相机,在场景、算法参数和图像参数都不变情况下,理论上只需要进行一次标定,可将标定表保存下来。
//不同相机图片标定出来的标定表不可复用,当场景改变或算法参数改变或图像参数改变时,需要重新标定。
nRet = MV_CC_LSCCalib(pUser, &stLSCCalib);
if (MV_OK != nRet)
{
printf("LSC Calib fail! nRet [0x%x]\n", nRet);
return;
}
//保存标定表到本地
FILE* fp_out = fopen("./LSCCalib.bin", "wb");
if (NULL == fp_out)
{
return ;
}
fwrite(stLSCCalib.pCalibBuf, 1, stLSCCalib.nCalibBufLen, fp_out);
fclose(fp_out);
g_IsNeedCalib = false;
}
// LSC校正
if (g_pDstData == NULL || g_nDstDataSize < pFrameInfo->nFrameLen)
{
if (g_pDstData)
{
free(g_pDstData);
g_pDstData = NULL;
g_nDstDataSize = 0;
}
g_pDstData = (unsigned char *)malloc(pFrameInfo->nFrameLen);
if (g_pDstData == NULL)
{
printf("malloc pDstData fail !\n");
return;
}
g_nDstDataSize = pFrameInfo->nFrameLen;
}
MV_CC_LSC_CORRECT_PARAM stLSCCorrectParam = {
0};
stLSCCorrectParam.nWidth = pFrameInfo->nWidth;
stLSCCorrectParam.nHeight = pFrameInfo->nHeight;
stLSCCorrectParam.enPixelType = pFrameInfo->enPixelType;
stLSCCorrectParam.pSrcBuf = pData;
stLSCCorrectParam.nSrcBufLen = pFrameInfo->nFrameLen;
stLSCCorrectParam.pDstBuf = g_pDstData;
stLSCCorrectParam.nDstBufSize = g_nDstDataSize;
stLSCCorrectParam.pCalibBuf = g_pCalibBuf;
stLSCCorrectParam.nCalibBufLen = g_nCalibBufSize;
nRet = MV_CC_LSCCorrect(pUser, &stLSCCorrectParam);
if (MV_OK != nRet)
{
printf("LSC Correct fail! nRet [0x%x]\n", nRet);
return;
}
if (pFrameInfo->nFrameNum < 10)
{
//保存图像到文件
MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam;
memset(&stSaveFileParam, 0, sizeof(MV_SAVE_IMG_TO_FILE_PARAM));
stSaveFileParam.enImageType = MV_Image_Bmp;
stSaveFileParam.enPixelType = pFrameInfo->enPixelType;
stSaveFileParam.nWidth = pFrameInfo->nWidth;
stSaveFileParam.nHeight = pFrameInfo->nHeight;
stSaveFileParam.nDataLen = pFrameInfo->nFrameLen;
stSaveFileParam.pData = pData;
sprintf_s(stSaveFileParam.pImagePath, 256, "BeforeImage_w%d_h%d_fn%03d.bmp", stSaveFileParam.nWidth, stSaveFileParam.nHeight, pFrameInfo->nFrameNum);
nRet = MV_CC_SaveImageToFile(pUser, &stSaveFileParam);
if (MV_OK != nRet)
{
printf("SaveImageToFile failed[%x]!\n", nRet);
return;
}
stSaveFileParam.pData = g_pDstData;
sprintf_s(stSaveFileParam.pImagePath, 256, "AfterImage_w%d_h%d_fn%03d.bmp", stSaveFileParam.nWidth, stSaveFileParam.nHeight, pFrameInfo->nFrameNum);
nRet = MV_CC_SaveImageToFile(pUser, &stSaveFileParam);
if (MV_OK != nRet)
{
printf("SaveImageToFile failed[%x]!\n", nRet);
return;
}
}
}
int main()
{
int nRet = MV_OK;
void* handle = NULL;
do
{
// ch:枚举设备 | en:Enum device
MV_CC_DEVICE_INFO_LIST stDeviceList;
memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
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_s("%d", &nIndex);
if (nIndex >= stDeviceList.nDeviceNum)
{
printf("Input error!\n");
break;
}
// ch:选择设备并创建句柄 | en:Select device and create handle
nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);
if (MV_OK != nRet)
{
printf("Create Handle fail! nRet [0x%x]\n", nRet);
break;
}
// ch:打开设备 | en:Open device
nRet = MV_CC_OpenDevice(handle);
if (MV_OK != nRet)
{
printf("Open Device fail! nRet [0x%x]\n", nRet);
break;
}
// ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
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);
}
}
// ch:设置触发模式为off | eb:Set trigger mode as off
nRet = MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF);
if (MV_OK != nRet)
{
printf("Set Trigger Mode fail! nRet [0x%x]\n", nRet);
break;
}
//判断是否可以本地导入
FILE* fp = fopen("./LSCCalib.bin", "rb");
if (fp)
{
int nFileLen = filelength(fileno(fp));
if (g_pCalibBuf == NULL || g_nCalibBufSize < nFileLen)
{
if (g_pCalibBuf)
{
free(g_pCalibBuf);
g_pCalibBuf = NULL;
g_nCalibBufSize = 0;
}
g_pCalibBuf = (unsigned char *)malloc(nFileLen);
if (g_pCalibBuf == NULL)
{
printf("malloc pCalibBuf fail !\n");
break;
}
g_nCalibBufSize = nFileLen;
}
fread(g_pCalibBuf, 1, g_nCalibBufSize, fp);
fclose(fp);
g_IsNeedCalib = false;
}
// ch:注册抓图回调 | en:Register image callback
nRet = MV_CC_RegisterImageCallBackEx(handle, ImageCallBackEx, handle);
if (MV_OK != nRet)
{
printf("Register Image CallBack fail! nRet [0x%x]\n", nRet);
break;
}
// ch:开始取流 | en:Start grab image
nRet = MV_CC_StartGrabbing(handle);
if (MV_OK != nRet)
{
printf("Start Grabbing fail! nRet [0x%x]\n", nRet);
break;
}
printf("Press a key to stop grabbing.\n");
WaitForKeyPress();
Sleep(1000);
// ch:停止取流 | en:Stop grab image
nRet = MV_CC_StopGrabbing(handle);
if (MV_OK != nRet)
{
printf("Stop Grabbing fail! nRet [0x%x]\n", nRet);
break;
}
// ch:关闭设备 | en:Close device
nRet = MV_CC_CloseDevice(handle);
if (MV_OK != nRet)
{
printf("Close Device fail! nRet [0x%x]\n", nRet);
break;
}
// ch:销毁句柄 | en:Destroy handle
nRet = MV_CC_DestroyHandle(handle);
if (MV_OK != nRet)
{
printf("Destroy Handle fail! nRet [0x%x]\n", nRet);
break;
}
} while (0);
if (g_pCalibBuf)
{
free(g_pCalibBuf);
g_pCalibBuf = NULL;
g_nCalibBufSize = 0;
}
if (g_pDstData)
{
free(g_pDstData);
g_pDstData = NULL;
g_nDstDataSize = 0;
}
if (nRet != MV_OK)
{
if (handle != NULL)
{
MV_CC_DestroyHandle(handle);
handle = NULL;
}
}
printf("Press a key to exit.\n");
WaitForKeyPress();
return 0;
}