海康工业相机对接彩色视频流

This commit is contained in:
zc
2026-03-11 15:05:14 +08:00
parent 4802e11f7c
commit c80dce6419
3 changed files with 66 additions and 106 deletions

View File

@@ -10,7 +10,7 @@ import top.wms.admin.controller.weighManage.ah.ScaleWebSocketHandler;
@Configuration @Configuration
@EnableWebSocket @EnableWebSocket
public class webSocket implements WebSocketConfigurer { public class WebSocketConfig implements WebSocketConfigurer {
@Autowired @Autowired
private CameraWebSocketHandler cameraWebSocketHandler; private CameraWebSocketHandler cameraWebSocketHandler;
@@ -19,7 +19,7 @@ public class webSocket implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册WebSocket端点允许所有跨域请求 // 注册WebSocket端点允许所有跨域请求
registry.addHandler(new ScaleWebSocketHandler(), "/ws/scale").setAllowedOrigins("*"); registry.addHandler(new ScaleWebSocketHandler(), "/ws/scale").setAllowedOrigins("*");
// 注册海康相机WebSocket端点 // 注册海康相机1 WebSocket端点
registry.addHandler(cameraWebSocketHandler, "/ws/camera").setAllowedOrigins("*"); registry.addHandler(cameraWebSocketHandler, "/ws/camera").setAllowedOrigins("*");
} }
} }

View File

@@ -76,8 +76,6 @@ public class CameraService {
if (null == stDeviceInfo) { if (null == stDeviceInfo) {
continue; continue;
} }
log.info("[camera {}}", i++);
printDeviceInfo(stDeviceInfo);
} }
// 默认选择第一个设备 // 默认选择第一个设备
@@ -110,6 +108,15 @@ public class CameraService {
return false; return false;
} }
// 设置Bayer转换质量用于彩色相机
log.info("Setting Bayer convert quality...");
nRet = MvCameraControl.MV_CC_SetBayerCvtQuality(hCamera, 1);
if (MV_OK != nRet) {
log.error("Set Bayer convert quality fail! nRet [0x{0}]", Integer.toHexString(nRet));
closeCamera();
return false;
}
log.info("Camera initialized successfully"); log.info("Camera initialized successfully");
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@@ -140,7 +147,6 @@ public class CameraService {
int nRet = MvCameraControl.MV_CC_RegisterImageCallBack(hCamera, new CameraImageCallBack() { int nRet = MvCameraControl.MV_CC_RegisterImageCallBack(hCamera, new CameraImageCallBack() {
@Override @Override
public int OnImageCallBack(byte[] bytes, MV_FRAME_OUT_INFO mv_frame_out_info) { public int OnImageCallBack(byte[] bytes, MV_FRAME_OUT_INFO mv_frame_out_info) {
printFrameInfo(mv_frame_out_info);
processImage(bytes, mv_frame_out_info); processImage(bytes, mv_frame_out_info);
return 0; return 0;
} }
@@ -226,7 +232,8 @@ public class CameraService {
/** /**
* 处理图像数据 * 处理图像数据
* @param bytes 原始图像数据 *
* @param bytes 原始图像数据
* @param frameInfo 帧信息 * @param frameInfo 帧信息
*/ */
private void processImage(byte[] bytes, MV_FRAME_OUT_INFO frameInfo) { private void processImage(byte[] bytes, MV_FRAME_OUT_INFO frameInfo) {
@@ -242,41 +249,65 @@ public class CameraService {
} }
lastFrameTime = currentTime; lastFrameTime = currentTime;
// 打印帧信息,以便调试 int dataSizeForRGB = frameInfo.width * frameInfo.height * 3; // RGB每个像素3字节
printFrameInfo(frameInfo); byte[] pDataForRGB = new byte[dataSizeForRGB];
// 对于黑白相机MV-CE050-30GM数据是单通道灰度格式 // 初始化像素转换参数
// 将原始灰度数据转换为BufferedImage MV_CC_PIXEL_CONVERT_PARAM_EX stConvertParam = new MV_CC_PIXEL_CONVERT_PARAM_EX();
BufferedImage image = new BufferedImage(frameInfo.width, frameInfo.height, BufferedImage.TYPE_BYTE_GRAY);
image.getRaster().setDataElements(0, 0, frameInfo.width, frameInfo.height, bytes); // 使用实际的width和height避免ExtendHeight为0的问题
stConvertParam.width = frameInfo.width;
stConvertParam.height = frameInfo.height;
stConvertParam.srcData = bytes;
stConvertParam.srcDataLen = frameInfo.frameLen;
stConvertParam.srcPixelType = frameInfo.pixelType;
stConvertParam.dstPixelType = MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
stConvertParam.dstBuffer = pDataForRGB;
stConvertParam.dstBufferSize = dataSizeForRGB;
// 尝试进行像素格式转换
int nRet = MvCameraControl.MV_CC_ConvertPixelTypeEx(hCamera, stConvertParam);
if (MV_OK != nRet) {
log.error("Convert PixelType fail, errCode: {}, srcPixelType: {}, width: {}, height: {}",
Integer.toHexString(nRet),
frameInfo.pixelType.getnValue(),
frameInfo.width,
frameInfo.height);
return;
}
// 将RGB数据转换为BufferedImage
BufferedImage image = new BufferedImage(frameInfo.width, frameInfo.height, BufferedImage.TYPE_3BYTE_BGR);
image.getRaster().setDataElements(0, 0, frameInfo.width, frameInfo.height, pDataForRGB);
// 压缩图像为JPEG格式 // 压缩图像为JPEG格式
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "jpeg", baos); ImageIO.write(image, "jpeg", baos);
byte[] compressedData = baos.toByteArray(); byte[] compressedData = baos.toByteArray();
baos.close(); baos.close();
log.debug("Original size: {} bytes, Compressed size: {} bytes, Compression ratio: {:.2f}%", log.debug("Original size: {} bytes, Converted size: {} bytes, Compressed size: {} bytes, Compression ratio: {:.2f}%",
bytes.length, compressedData.length, (1 - (double)compressedData.length / bytes.length) * 100); bytes.length, pDataForRGB.length, compressedData.length, (1 - (double) compressedData.length / bytes.length) * 100);
// 创建包含图像尺寸信息和压缩数据的消息 // 创建包含图像尺寸信息和压缩数据的消息
// 消息格式: [width(4 bytes)][height(4 bytes)][image data] // 消息格式: [width(4 bytes)][height(4 bytes)][image data]
byte[] message = new byte[8 + compressedData.length]; byte[] message = new byte[8 + compressedData.length];
// 写入宽度和高度(使用小端序) // 写入宽度和高度(使用小端序)
message[0] = (byte) (frameInfo.width & 0xFF); message[0] = (byte) (frameInfo.width & 0xFF);
message[1] = (byte) ((frameInfo.width >> 8) & 0xFF); message[1] = (byte) ((frameInfo.width >> 8) & 0xFF);
message[2] = (byte) ((frameInfo.width >> 16) & 0xFF); message[2] = (byte) ((frameInfo.width >> 16) & 0xFF);
message[3] = (byte) ((frameInfo.width >> 24) & 0xFF); message[3] = (byte) ((frameInfo.width >> 24) & 0xFF);
message[4] = (byte) (frameInfo.height & 0xFF); message[4] = (byte) (frameInfo.height & 0xFF);
message[5] = (byte) ((frameInfo.height >> 8) & 0xFF); message[5] = (byte) ((frameInfo.height >> 8) & 0xFF);
message[6] = (byte) ((frameInfo.height >> 16) & 0xFF); message[6] = (byte) ((frameInfo.height >> 16) & 0xFF);
message[7] = (byte) ((frameInfo.height >> 24) & 0xFF); message[7] = (byte) ((frameInfo.height >> 24) & 0xFF);
// 写入压缩后的图像数据 // 写入压缩后的图像数据
System.arraycopy(compressedData, 0, message, 8, compressedData.length); System.arraycopy(compressedData, 0, message, 8, compressedData.length);
// 发送图像数据到所有连接的WebSocket客户端 // 发送图像数据到所有连接的WebSocket客户端
for (WebSocketSession session : webSocketSessions.values()) { for (WebSocketSession session : webSocketSessions.values()) {
if (session.isOpen()) { if (session.isOpen()) {
@@ -346,59 +377,4 @@ public class CameraService {
return isRunning; return isRunning;
} }
/**
* 打印设备信息
* @param stDeviceInfo 设备信息
*/
private void printDeviceInfo(MV_CC_DEVICE_INFO stDeviceInfo) {
if (null == stDeviceInfo) {
log.error("stDeviceInfo is null");
return;
}
if ((stDeviceInfo.transportLayerType == MV_GIGE_DEVICE) || (stDeviceInfo.transportLayerType == MV_GENTL_GIGE_DEVICE)) {
log.info("\tCurrentIp: {}", stDeviceInfo.gigEInfo.currentIp);
log.info("\tModel: {}", stDeviceInfo.gigEInfo.modelName);
log.info("\tUserDefinedName: {}", stDeviceInfo.gigEInfo.userDefinedName);
} else if (stDeviceInfo.transportLayerType == MV_USB_DEVICE) {
log.info("\tUserDefinedName: {}", stDeviceInfo.usb3VInfo.userDefinedName);
log.info("\tSerial Number: {}", stDeviceInfo.usb3VInfo.serialNumber);
log.info("\tDevice Number: {}", stDeviceInfo.usb3VInfo.deviceNumber);
} else if (stDeviceInfo.transportLayerType == MV_GENTL_CAMERALINK_DEVICE) {
log.info("\tUserDefinedName: {}", stDeviceInfo.cmlInfo.userDefinedName);
log.info("\tSerial Number: {}", stDeviceInfo.cmlInfo.serialNumber);
log.info("\tDevice Number: {}", stDeviceInfo.cmlInfo.DeviceID);
} else if (stDeviceInfo.transportLayerType == MV_GENTL_CXP_DEVICE) {
log.info("\tUserDefinedName: {}", stDeviceInfo.cxpInfo.userDefinedName);
log.info("\tSerial Number: {}", stDeviceInfo.cxpInfo.serialNumber);
log.info("\tDevice Number: {}", stDeviceInfo.cxpInfo.DeviceID);
} else if (stDeviceInfo.transportLayerType == MV_GENTL_XOF_DEVICE) {
log.info("\tUserDefinedName: {}", stDeviceInfo.xofInfo.userDefinedName);
log.info("\tSerial Number: {}", stDeviceInfo.xofInfo.serialNumber);
log.info("\tDevice Number: {}", stDeviceInfo.xofInfo.DeviceID);
} else {
log.error("Device is not supported! ");
}
log.info("\tAccessible: {}", MvCameraControl.MV_CC_IsDeviceAccessible(stDeviceInfo, MV_ACCESS_Exclusive));
}
/**
* 打印帧信息
* @param stFrameInfo 帧信息
*/
private void printFrameInfo(MV_FRAME_OUT_INFO stFrameInfo) {
if (null == stFrameInfo) {
log.error("stFrameInfo is null");
return;
}
StringBuilder frameInfo = new StringBuilder("");
frameInfo.append(("\tFrameNum[" + stFrameInfo.frameNum + "]"));
frameInfo.append("\tWidth[" + stFrameInfo.width + "]");
frameInfo.append("\tHeight[" + stFrameInfo.height + "]");
frameInfo.append(String.format("\tPixelType[%#x]", stFrameInfo.pixelType.getnValue()));
log.debug(frameInfo.toString());
}
} }

View File

@@ -140,36 +140,20 @@ public class ConvertPixelType
// Choose a device to operate // Choose a device to operate
int camIndex = -1; int camIndex = -1;
while (true) while (true) {
{ try {
System.out.print("Please input camera index:"); camIndex = 0;
if (scanner.hasNextInt()) if ((camIndex >= 0 && camIndex < stDeviceList.size()) || -1 == camIndex) {
{ break;
try } else {
{ System.out.println("Input error: " + camIndex + " Over Range:( 0 - " + (stDeviceList.size() - 1) + " )");
camIndex = scanner.nextInt();
if ((camIndex >= 0 && camIndex < stDeviceList.size()) || -1 == camIndex)
{
break;
}
else
{
System.out.println("Input error: " + camIndex + " Over Range:( 0 - " + (stDeviceList.size()-1) + " )");
}
} }
catch (Exception e) } catch (Exception e) {
{ System.out.println("Input not number.");
System.out.println("Input not number."); camIndex = -1;
camIndex = -1;
break;
}
}
else
{
camIndex = -1;
break; break;
} }
} }
if (-1 == camIndex) { if (-1 == camIndex) {
System.out.println("Input error.exit"); System.out.println("Input error.exit");
@@ -328,7 +312,7 @@ public class ConvertPixelType
System.err.printf("set Bayer convert quality fail! nRet [0x%x]\n", nRet); System.err.printf("set Bayer convert quality fail! nRet [0x%x]\n", nRet);
break; break;
} }
System.out.println("临时日志: frameInfo.width = " + stImageInfo.width + ", frameInfo.height = " + stImageInfo.height + ", frameInfo.ExtendWidth = " + stImageInfo.ExtendWidth + ", frameInfo.ExtendHeight = " + stImageInfo.ExtendHeight + ", frameInfo.frameLen = " + stImageInfo.frameLen + ", frameInfo.pixelType = " + stImageInfo.pixelType);
int dataSizeForRGB = stImageInfo.width * stImageInfo.height * 3; // every RGB pixel takes 3 bytes int dataSizeForRGB = stImageInfo.width * stImageInfo.height * 3; // every RGB pixel takes 3 bytes
byte[] pDataForRGB = new byte[dataSizeForRGB]; byte[] pDataForRGB = new byte[dataSizeForRGB];