今天出来冒个泡吧!多话不说,理论也不讲。
海康工业相机BayerRG8转BGR8
开发板找到能用的C++代码:
//海康PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
int Bayer8ToRgb24(unsigned char* pbBayer, int dwWidth, int dwHeight, unsigned char* pbRgb, int iRgbBufLen)
{
if (nullptr == pbBayer || nullptr == pbRgb)
{
return 0;
}
if (dwWidth <= 0 || dwHeight <= 0 || iRgbBufLen <= 0)
{
return 0;
}
if (iRgbBufLen != dwWidth * dwHeight * 3)
{
return 0;
}
unsigned char* m_pBay = pbBayer;
unsigned char* m_pRGB24 = pbRgb;
unsigned long m_ulWidth = dwWidth;
unsigned long m_ulHeight = dwHeight;
#define R(x,y) m_pRGB24[2 + 3 * ((x) + m_ulWidth * (y))]
#define G(x,y) m_pRGB24[1 + 3 * ((x) + m_ulWidth * (y))]
#define B(x,y) m_pRGB24[0 + 3 * ((x) + m_ulWidth * (y))]
#define Bay(x,y) m_pBay[(x) + m_ulWidth * (y)]
auto bayer_copy = [&](int x, int y)
{
G(x + 0, y + 0) = Bay(x + 0, y + 0);
G(x + 1, y + 1) = Bay(x + 1, y + 1);
G(x + 0, y + 1) = G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 1, y + 1)) / 2;
R(x + 0, y + 0) = R(x + 1, y + 0) = R(x + 1, y + 1) = R(x + 0, y + 1) = Bay(x + 0, y + 1);
B(x + 1, y + 1) = B(x + 0, y + 0) = B(x + 0, y + 1) = B(x + 1, y + 0) = Bay(x + 1, y + 0);
};
auto bayer_bilinear = [&](int x, int y)
{
R(x + 0, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 0, y - 1)) / 2;
G(x + 0, y + 0) = Bay(x + 0, y + 0);
B(x + 0, y + 0) = ((unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 0)) / 2;
R(x + 0, y + 1) = Bay(x + 0, y + 1);
G(x + 0, y + 1) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 0, y + 2) + (unsigned long)Bay(x - 1, y + 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
B(x + 0, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 2) + (unsigned long)Bay(x - 1, y + 2)) / 4;
R(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1) + (unsigned long)Bay(x + 0, y - 1) + (unsigned long)Bay(x + 2, y - 1)) / 4;
G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 2, y + 0) + (unsigned long)Bay(x + 1, y - 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
B(x + 1, y + 0) = Bay(x + 1, y + 0); R(x + 1, y + 1) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1)) / 2; G(x + 1, y + 1) = Bay(x + 1, y + 1);
B(x + 1, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x + 1, y + 2)) / 2;
};
int i, j;
for (i = 0; i < m_ulWidth; i += 2)
{
for (j = 0; j < m_ulHeight; j += 2)
{
if (i == 0 || j == 0 || i == m_ulWidth - 2 || j == m_ulHeight - 2)
{
bayer_copy(i, j);
}
else
{
bayer_bilinear(i, j);
}
}
}
return 1;
}
用海康SDK搞:
//海康api PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
bool Convert2Mat(void* handle, stImageNode& pstImage, uint8_t** pBgrData)
{
if (NULL == pstImage.pData)
{
return false; //return MV_E_PARAMETER;
}
unsigned int nDataSizeForBGR = pstImage.nWidth * pstImage.nHeight * 3;
*pBgrData = (unsigned char*)malloc(nDataSizeForBGR);
if (NULL == *pBgrData)
{
return false; // return MV_E_BUFOVER;
}
// ch:像素格式转换 | en:Convert pixel format
MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));
stConvertParam.nWidth = pstImage.nWidth; //ch:图像宽 | en:image width
stConvertParam.nHeight = pstImage.nHeight; //ch:图像高 | en:image height
stConvertParam.pSrcData = pstImage.pData; //ch:输入数据缓存 | en:input data buffer
stConvertParam.nSrcDataLen = pstImage.nFrameLen; //ch:输入数据大小 | en:input data size
stConvertParam.enSrcPixelType = pstImage.enPixelType; //ch:输入像素格式 | en:input pixel format
stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed; //ch:输出像素格式 | en:output pixel format
stConvertParam.pDstBuffer = *pBgrData; //ch:输出数据缓存 | en:output data buffer
stConvertParam.nDstBufferSize = nDataSizeForBGR; //ch:输出缓存大小 | en:output buffer size
int nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
if (MV_OK != nRet)
{
free(*pBgrData);
printf("++++++++++++++++++++++error nFrameNum [%d] \r\n", pstImage.nFrameNum);
return false; //return MV_E_UNKNOW;
}
return true;
//注意在外面要free pBgrData
}
完整的全帧率相机抓拍取图程序:
#include <stdio.h>
#ifdef _WIN32
#include <Windows.h>
#include <conio.h>
#include <process.h>
#endif
#include <iostream>
#include <fstream>
#include <sstream>
#include <random>
#include <queue>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <opencv2/opencv.hpp>
#include "MvCameraControl.h"
//缓存队列
template<class T>
class DataQueue
{
public:
DataQueue(void)
{
};
virtual ~DataQueue(void)
{
clear();
};
public:
bool push(T& data)
{
std::unique_lock<std::mutex> lock(m_mutex);
m_queue.push(std::move(data));
return true;
};
bool pop(T& data)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_queue.empty())
return false;
data = m_queue.front();
m_queue.pop();
return true;
};
void clear()
{
std::unique_lock<std::mutex> lock(m_mutex);
while (!m_queue.empty())
{
T& data = m_queue.front();
m_queue.pop();
}
};
int size()
{
std::unique_lock<std::mutex> lock(m_mutex);
return m_queue.size();
};
protected:
std::mutex m_mutex;
std::queue<T> m_queue;
};
//信号量工具
class Semaphore
{
std::mutex mx;
std::condition_variable cond;
long count;
public:
Semaphore(long count = 0) : count(count) {};
~Semaphore(void) { post(); };
void wait(void)
{
std::unique_lock<std::mutex>lock(mx);
cond.wait(lock, [&]() {return count > 0; });
--count;
};
bool wait(int stime)
{
std::unique_lock<std::mutex>lock(mx);
bool bRet = cond.wait_for(lock, std::chrono::seconds(stime), [&]() {return count > 0; });
--count;
return bRet;
};
void post(void)
{
std::unique_lock<std::mutex>lock(mx);
++count;
cond.notify_one();
};
};
//中间缓存图像格式
typedef struct _stImageNode_
{
unsigned char* pData; //图像数据
uint64_t nFrameLen;
unsigned int nWidth;
unsigned int nHeight;
unsigned int nFrameNum;
enum MvGvspPixelType enPixelType; //enum MvGvspPixelType
_stImageNode_()
{
pData = 0;
nFrameLen = nWidth = nHeight = nFrameNum = 0;
enPixelType = PixelType_Gvsp_BayerGB8;
}
~_stImageNode_()
{
if (pData != NULL)
{
free(pData);
}
}
} stImageNode;
//海康PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
int Bayer8ToRgb24(unsigned char* pbBayer, int dwWidth, int dwHeight, unsigned char* pbRgb, int iRgbBufLen)
{
if (nullptr == pbBayer || nullptr == pbRgb)
{
return 0;
}
if (dwWidth <= 0 || dwHeight <= 0 || iRgbBufLen <= 0)
{
return 0;
}
if (iRgbBufLen != dwWidth * dwHeight * 3)
{
return 0;
}
unsigned char* m_pBay = pbBayer;
unsigned char* m_pRGB24 = pbRgb;
unsigned long m_ulWidth = dwWidth;
unsigned long m_ulHeight = dwHeight;
#define R(x,y) m_pRGB24[2 + 3 * ((x) + m_ulWidth * (y))]
#define G(x,y) m_pRGB24[1 + 3 * ((x) + m_ulWidth * (y))]
#define B(x,y) m_pRGB24[0 + 3 * ((x) + m_ulWidth * (y))]
#define Bay(x,y) m_pBay[(x) + m_ulWidth * (y)]
auto bayer_copy = [&](int x, int y)
{
G(x + 0, y + 0) = Bay(x + 0, y + 0);
G(x + 1, y + 1) = Bay(x + 1, y + 1);
G(x + 0, y + 1) = G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 1, y + 1)) / 2;
R(x + 0, y + 0) = R(x + 1, y + 0) = R(x + 1, y + 1) = R(x + 0, y + 1) = Bay(x + 0, y + 1);
B(x + 1, y + 1) = B(x + 0, y + 0) = B(x + 0, y + 1) = B(x + 1, y + 0) = Bay(x + 1, y + 0);
};
auto bayer_bilinear = [&](int x, int y)
{
R(x + 0, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 0, y - 1)) / 2;
G(x + 0, y + 0) = Bay(x + 0, y + 0);
B(x + 0, y + 0) = ((unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 0)) / 2;
R(x + 0, y + 1) = Bay(x + 0, y + 1);
G(x + 0, y + 1) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 0, y + 2) + (unsigned long)Bay(x - 1, y + 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
B(x + 0, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 2) + (unsigned long)Bay(x - 1, y + 2)) / 4;
R(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1) + (unsigned long)Bay(x + 0, y - 1) + (unsigned long)Bay(x + 2, y - 1)) / 4;
G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 2, y + 0) + (unsigned long)Bay(x + 1, y - 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
B(x + 1, y + 0) = Bay(x + 1, y + 0); R(x + 1, y + 1) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1)) / 2; G(x + 1, y + 1) = Bay(x + 1, y + 1);
B(x + 1, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x + 1, y + 2)) / 2;
};
int i, j;
for (i = 0; i < m_ulWidth; i += 2)
{
for (j = 0; j < m_ulHeight; j += 2)
{
if (i == 0 || j == 0 || i == m_ulWidth - 2 || j == m_ulHeight - 2)
{
bayer_copy(i, j);
}
else
{
bayer_bilinear(i, j);
}
}
}
return 1;
}
//海康api PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
bool Convert2Mat(void* handle, stImageNode& pstImage, uint8_t** pBgrData)
{
if (NULL == pstImage.pData)
{
return false; //return MV_E_PARAMETER;
}
unsigned int nDataSizeForBGR = pstImage.nWidth * pstImage.nHeight * 3;
*pBgrData = (unsigned char*)malloc(nDataSizeForBGR);
if (NULL == *pBgrData)
{
return false; // return MV_E_BUFOVER;
}
// ch:像素格式转换 | en:Convert pixel format
MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));
stConvertParam.nWidth = pstImage.nWidth; //ch:图像宽 | en:image width
stConvertParam.nHeight = pstImage.nHeight; //ch:图像高 | en:image height
stConvertParam.pSrcData = pstImage.pData; //ch:输入数据缓存 | en:input data buffer
stConvertParam.nSrcDataLen = pstImage.nFrameLen; //ch:输入数据大小 | en:input data size
stConvertParam.enSrcPixelType = pstImage.enPixelType; //ch:输入像素格式 | en:input pixel format
stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed; //ch:输出像素格式 | en:output pixel format
stConvertParam.pDstBuffer = *pBgrData; //ch:输出数据缓存 | en:output data buffer
stConvertParam.nDstBufferSize = nDataSizeForBGR; //ch:输出缓存大小 | en:output buffer size
int nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
if (MV_OK != nRet)
{
free(*pBgrData);
printf("++++++++++++++++++++++error nFrameNum [%d] \r\n", pstImage.nFrameNum);
return false; //return MV_E_UNKNOW;
}
return true;
//注意在外面要free pBgrData
}
//全局变量
DataQueue<stImageNode*> m_list;
Semaphore m_sem;
int main()
{
#ifdef _WIN32
system("rd /s /q img");
system("md img");
#else
system("rm -rf img");
system("mkdir img");
#endif
printf("app start *********************\n");
std::atomic_bool m_stated;
std::thread m_thread;
MV_CC_DEVICE_INFO_LIST stDeviceList;
memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE, &stDeviceList);
if (MV_OK != nRet || stDeviceList.nDeviceNum < 1)
{
return -1;
}
int nIndex = 0;
void* handle = NULL;
MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);//nIndex=0
MV_CC_OpenDevice(handle);
MV_CC_SetIntValueEx(handle, "Width", 1920);
MV_CC_SetIntValueEx(handle, "Height", 1080);
MV_CC_SetIntValueEx(handle, "OffsetX", 64);
MV_CC_SetIntValueEx(handle, "OffsetY", 484);
MV_CC_SetEnumValueByString(handle, "PixelFormat", "BayerGB8"); //可以实现90fps取图
//MV_CC_SetEnumValueByString(handle, "PixelFormat", "RGB8Packed"); //只有20多fps
MVCC_INTVALUE_EX stIntEx = { 0 };
MV_CC_GetIntValueEx(handle, "Width", &stIntEx);
int nWidth = stIntEx.nCurValue;
memset(&stIntEx, 0, sizeof(MVCC_INTVALUE_EX));
MV_CC_GetIntValueEx(handle, "Height", &stIntEx);
int nHeight = stIntEx.nCurValue;
MV_CC_GetIntValueEx(handle, "PayloadSize", &stIntEx);
uint64_t m_nImageSize = stIntEx.nCurValue;
MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF); //设置触发模式为off
MV_CC_SetBoolValue(handle, "CCMEnable", false); //CCM关闭
MV_CC_SetBoolValue(handle, "ColorTransformationEnable", false); //颜色变化关闭
MV_CC_SetBoolValue(handle, "HueEnable", false); //hue关闭
MV_CC_SetBoolValue(handle, "SaturationEnable", false); //饱和度关闭
MV_CC_SetBoolValue(handle, "BlackLevelEnable", false); //关闭黑白电平
MV_CC_SetIntValueEx(handle, "Brightness", 90);
MV_CC_SetIntValueEx(handle, "AutoExposureTimeLowerLimit", 30);
MV_CC_SetIntValueEx(handle, "AutoExposureTimeUpperLimit", 11000);
MV_CC_SetEnumValueByString(handle, "ExposureAuto", "Continuous"); //Off-手动 Continuous-自动
MV_CC_SetFloatValue(handle, "ExposureTime", 11111.0000); //曝光设置 1000/90约11ms曝光
MV_CC_SetEnumValueByString(handle, "GainAuto", "Off"); //Off-手动 Continuous-自动
MV_CC_SetFloatValue(handle, "Gain", 0.0000); //增益0-12
//注册抓图回调
MV_CC_RegisterImageCallBackEx(handle, [](unsigned char* pData, MV_FRAME_OUT_INFO_EX * pFrameInfo, void* pUser) {
if (NULL == pFrameInfo || NULL == pData)
{
printf("ImageCallBackEx Input Param invalid.\n");
return;
}
//相机内存数据存队列
stImageNode* sImage = new stImageNode();
sImage->enPixelType = pFrameInfo->enPixelType;
sImage->nFrameLen = pFrameInfo->nFrameLenEx;
sImage->nWidth = pFrameInfo->nWidth;
sImage->nHeight = pFrameInfo->nHeight;
sImage->nFrameNum = pFrameInfo->nFrameNum + 1;
sImage->pData = (unsigned char*)malloc(sImage->nHeight * sImage->nWidth * 3);
memcpy(sImage->pData, pData, sImage->nFrameLen);
//通知存图
m_list.push(sImage);
m_sem.post();
}, NULL);
printf("app init ok *********************\n");
auto cutstart = [&] {
//先关线程
m_stated.store(false);
if (m_thread.joinable())
m_thread.join();
//开始取流API
MV_CC_StartGrabbing(handle);
m_stated.store(true);
//下面建议起多个比如6个线程写会快很多
m_thread = std::move(std::thread([&]()
{
printf("WorkThread Begin . \r\n");
while (m_stated)
{
m_sem.wait();
stImageNode* sImage = NULL;
if (!m_list.pop(sImage))
{
printf("Poll failed, maybe no data. \r\n");
std::this_thread::sleep_for(std::chrono::seconds(2));
continue;
}
else
{
//根据实际场景需求,对图像进行 处理
#if 1
cv::Mat cvImage(sImage->nHeight, sImage->nWidth, CV_8UC3, cv::Scalar(0, 0, 0));
//BayerGB8转RGB格式 不过建议用下面方法 SDK的MV_CC_ConvertPixelType做可以节约CPU
Bayer8ToRgb24((unsigned char*)(sImage->pData), sImage->nWidth, sImage->nHeight, cvImage.data, cvImage.step * sImage->nHeight);
//下面写jpg存图
char szFileName[256] = { 0 };
sprintf(szFileName, "img/Image_%d_%d_%d.jpg", sImage->nWidth, sImage->nHeight, sImage->nFrameNum);
printf("write >>> %s\n", szFileName);
cv::imwrite(szFileName, cvImage); //cv::imshow("cvImage", cvImage); //cv::waitKey();
#endif
#if 0
//SDK库BayerGB8转RGB格式
uint8_t* pBgrData = NULL;
cv::Mat cvImage;
Convert2Mat(handle, *sImage, &pBgrData);
cvImage = cv::Mat(sImage->nHeight, sImage->nWidth, CV_8UC3, pBgrData);
//下面写jpg存图
char szFileName[256] = { 0 };
sprintf(szFileName, "img/Image_%d_%d_%d.jpg", Image.nWidth, Image.nHeight, Image.nFrameNum);
printf("write >>> %s\n", szFileName);
cv::imwrite(szFileName, cvImage);
//删除 Convert2Mat里面拷贝的数据
if (pBgrData != NULL)
{
free(pBgrData);
pBgrData = NULL;
}
#endif
#if 0
//原始raw格式
FILE* fp = NULL;
char szFileName[256] = { 0 };
sprintf(szFileName, "img/Image_%d_%d_%d.raw", sImage->nWidth, sImage->nHeight, sImage->nFrameNum);
printf("write %s\n", szFileName);
fp = fopen(szFileName, "wb+");
if (fp == NULL)
{
//return MV_E_RESOURCE;
}
fwrite(sImage->pData, 1, sImage->nFrameLen, fp);
fclose(fp);
fp = NULL;
#endif
cvImage.release();
//删除释放数据
if (sImage->pData)
{
free(sImage->pData);
sImage->pData = NULL;
}
delete sImage;
}
}
printf("WorkThread exit . \r\n");
}));
};
auto cutend = [&] {
MV_CC_StopGrabbing(handle); //停止取流
MV_CC_RegisterImageCallBackEx(handle, NULL, NULL); //注销抓图回调
printf("**************************************\n");
printf("cut end*******************************\n");
printf("**************************************\n");
printf("\n\n\n");
};
cutstart();
std::this_thread::sleep_for(std::chrono::seconds(5));
cutend();
while (1)
{
char c = getchar(); //按键会阻塞
if ('q' == c) //退出程序
break;
else if ('b' == c)
{
cutstart();
}
else if ('e' == c)
{
cutend();
}
else if ('c' == c)
{
m_sem.post();
m_stated.store(false);
if (m_thread.joinable())
m_thread.join();
MV_CC_CloseDevice(handle); //关闭设备
MV_CC_DestroyHandle(handle); //销毁句柄
MV_CC_Finalize(); //反初始化SDK
}
else
{
//printf("**************************************\n");
}
}
printf("app end ok *********************\n");
//scanf("\n");
return 0;
}