11 changed files with 420 additions and 4 deletions
-
2build.gradle
-
BINlib/opencv-420.jar
-
4src/main/java/com/iflytop/gd/app/controller/PhotoController.java
-
46src/main/java/com/iflytop/gd/app/core/runner/BaslerCameraApplicationRunner.java
-
15src/main/java/com/iflytop/gd/app/service/api/PhotosService.java
-
BINsrc/main/java/com/iflytop/gd/camera/BaslerCameraWrapper$GrabResult.class
-
BINsrc/main/java/com/iflytop/gd/camera/BaslerCameraWrapper.class
-
130src/main/java/com/iflytop/gd/camera/BaslerCameraWrapper.java
-
65src/main/java/com/iflytop/gd/hardware/controller/BaslerCameraController.java
-
148src/main/java/com/iflytop/gd/hardware/drivers/CameraBaslerDriver.java
-
12src/main/resources/application-dev.yml
@ -0,0 +1,46 @@ |
|||
package com.iflytop.gd.app.core.runner; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.ApplicationArguments; |
|||
import org.springframework.boot.ApplicationRunner; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Slf4j |
|||
@Order(2) |
|||
@Component |
|||
public class BaslerCameraApplicationRunner implements ApplicationRunner { |
|||
@Value("${opencv.library-path}") |
|||
private String opencvLibraryPath; |
|||
|
|||
@Value("${pylon.library-path}") |
|||
private String pylonLibraryPath; |
|||
|
|||
@Value("${pylon.wrapper-path}") |
|||
private String pylonWrapperPath; |
|||
|
|||
@Override |
|||
public void run(ApplicationArguments args) throws Exception { |
|||
try { |
|||
System.load(this.opencvLibraryPath); |
|||
log.info("成功加载OpenCV库: {}", this.opencvLibraryPath); |
|||
} catch (UnsatisfiedLinkError | SecurityException e) { |
|||
log.error("加载OpenCV库失败: {}", this.opencvLibraryPath, e); |
|||
} |
|||
|
|||
try { |
|||
System.load(this.pylonLibraryPath); |
|||
log.info("成功加载pylon库: {}", this.pylonLibraryPath); |
|||
} catch (UnsatisfiedLinkError | SecurityException e) { |
|||
log.error("加载pylon库失败: {}", this.pylonLibraryPath, e); |
|||
} |
|||
try { |
|||
System.load(this.pylonWrapperPath); |
|||
log.info("成功加载pylon wrapper库: {}", this.pylonWrapperPath); |
|||
} catch (UnsatisfiedLinkError | SecurityException e) { |
|||
log.error("加载pylon wrapper库失败: {}", this.pylonWrapperPath, e); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,130 @@ |
|||
package com.iflytop.gd.camera; |
|||
|
|||
public class BaslerCameraWrapper { |
|||
public static final int ACCESS_MODE_MONITOR = 0; |
|||
public static final int ACCESS_MODE_CONTROL = 1; |
|||
public static final int ACCESS_MODE_STREAM = (1 << 1); |
|||
public static final int ACCESS_MODE_EVENT = (1 << 2); |
|||
public static final int ACCESS_MODE_EXCLUSIVE = (1 << 3); |
|||
|
|||
public static class GrabResult { |
|||
public byte[] imageBuffer; |
|||
public int payloadType; |
|||
public int pixelType; |
|||
public int sizeX; |
|||
public int sizeY; |
|||
public int offsetX; |
|||
public int offsetY; |
|||
public int paddingX; |
|||
public int paddingY; |
|||
public long PayloadSize; |
|||
public int ErrorCode; |
|||
} |
|||
|
|||
/** |
|||
* Initializes the pylon runtime system. |
|||
*/ |
|||
public native void initialize(); |
|||
|
|||
/** |
|||
* Enumerates all camera devices. |
|||
* @return The number of found devices. |
|||
*/ |
|||
public native int enumerateDevices(); |
|||
|
|||
/** |
|||
* Terminates the pylon runtime system. |
|||
*/ |
|||
public native void terminate(); |
|||
|
|||
/** |
|||
* Creates a camera device by index. |
|||
* @param index The index of the camera device. |
|||
* @return The handle of the camera device. |
|||
*/ |
|||
public native long createDeviceByIndex(int index); |
|||
|
|||
/** |
|||
* Opens the camera device. |
|||
* @param hDev The handle of the camera device. |
|||
* @param accessMode The access mode. |
|||
*/ |
|||
public native void deviceOpen(long hDev, int accessMode); |
|||
|
|||
/** |
|||
* Checks if a camera device feature is readable. |
|||
* @param hDev The handle of the camera device. |
|||
* @param name The name of the feature. |
|||
* @return true if the feature is readable, false otherwise. |
|||
*/ |
|||
public native boolean deviceFeatureIsReadable(long hDev, String name); |
|||
|
|||
/** |
|||
* Read a camera device feature to a string. |
|||
* @param hDev The handle of the camera device. |
|||
* @param name The name of the feature. |
|||
* @param size The size of the string buffer. |
|||
* @return The string value of the feature. |
|||
*/ |
|||
public native String deviceFeatureToString(long hDev, String name, int size); |
|||
|
|||
/** |
|||
* Checks if a camera device feature is available. |
|||
* @param hDev The handle of the camera device. |
|||
* @param name The name of the feature. |
|||
* @return true if the feature is available, false otherwise. |
|||
*/ |
|||
public native boolean deviceFeatureIsAvailable(long hDev, String name); |
|||
|
|||
/** |
|||
* Writes a camera device feature from a string. |
|||
* @param hDev The handle of the camera device. |
|||
* @param name The name of the feature. |
|||
* @param value The value of the feature. |
|||
*/ |
|||
public native void deviceFeatureFromString(long hDev, String name, String value); |
|||
|
|||
/** |
|||
* Checks if a camera device feature is writable. |
|||
* @param hDev The handle of the camera device. |
|||
* @param name The name of the feature. |
|||
* @return true if the feature is writable, false otherwise. |
|||
*/ |
|||
public native boolean deviceFeatureIsWritable(long hDev, String name); |
|||
|
|||
/** |
|||
* Writes a camera device feature from an integer. |
|||
* @param hDev The handle of the camera device. |
|||
* @param name The name of the feature. |
|||
* @param value The value of the feature. |
|||
*/ |
|||
public native void deviceSetIntegerFeature(long hDev, String name, int value); |
|||
|
|||
/** |
|||
* Reads a camera device feature to an integer. |
|||
* @param hDev The handle of the camera device. |
|||
* @param name The name of the feature. |
|||
* @return The integer value of the feature. |
|||
*/ |
|||
public native int deviceGetIntegerFeatureInt32(long hDev, String name); |
|||
|
|||
/** |
|||
* Grabs a single frame from the camera device. |
|||
* @param hDev The handle of the camera device. |
|||
* @param channel The channel index. |
|||
* @return The grab result. |
|||
*/ |
|||
public native GrabResult deviceGrabSingleFrame(long hDev, int channel); |
|||
|
|||
/** |
|||
* Closes the camera device. |
|||
* @param hDev The handle of the camera device. |
|||
*/ |
|||
public native void deviceClose(long hDev); |
|||
|
|||
/** |
|||
* Destroys the camera device. |
|||
* @param hDev The handle of the camera device. |
|||
*/ |
|||
public native void destroyDevice(long hDev); |
|||
} |
@ -0,0 +1,65 @@ |
|||
package com.iflytop.gd.hardware.controller; |
|||
|
|||
import com.iflytop.gd.hardware.drivers.CameraBaslerDriver; |
|||
import io.swagger.v3.oas.annotations.tags.Tag; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.opencv.core.Mat; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
@Slf4j |
|||
@Tag(name = "Basler相机驱动控制器") |
|||
@RestController |
|||
@RequestMapping("/api/basler") |
|||
@RequiredArgsConstructor |
|||
public class BaslerCameraController { |
|||
|
|||
@Autowired |
|||
private CameraBaslerDriver driver; |
|||
|
|||
// 设置相机索引 |
|||
@PostMapping("/setIndex") |
|||
public ResponseEntity<String> setIndex(@RequestParam Integer index) { |
|||
driver.setIndex(index); |
|||
log.info("设置相机索引为: {}", index); |
|||
return ResponseEntity.ok("设置相机索引为: " + index); |
|||
} |
|||
|
|||
// 使能相机 |
|||
@PostMapping("/enable") |
|||
public ResponseEntity<String> enable() { |
|||
driver.enable(); |
|||
return ResponseEntity.ok("相机已使能"); |
|||
} |
|||
|
|||
// 关闭相机 |
|||
@PostMapping("/disable") |
|||
public ResponseEntity<String> disable() { |
|||
driver.disable(); |
|||
return ResponseEntity.ok("相机已关闭"); |
|||
} |
|||
|
|||
// 抓取Mat对象(仅演示返回字符串,实际可自定义返回格式) |
|||
@GetMapping("/grabToMat") |
|||
public ResponseEntity<String> grabToMat() { |
|||
Mat mat = driver.grabToMat(); |
|||
log.info("抓取Mat成功,尺寸: {}x{}", mat.rows(), mat.cols()); |
|||
return ResponseEntity.ok("抓取Mat成功,尺寸: " + mat.rows() + "x" + mat.cols()); |
|||
} |
|||
|
|||
// 保存图片 |
|||
@PostMapping("/saveImg") |
|||
public ResponseEntity<String> saveImg(@RequestParam String filePath) { |
|||
driver.saveImg(filePath); |
|||
return ResponseEntity.ok("图片已保存到: " + filePath); |
|||
} |
|||
|
|||
// 保存彩色图片 |
|||
@PostMapping("/saveColorImg") |
|||
public ResponseEntity<String> saveColorImg(@RequestParam String filePath) { |
|||
driver.saveColorImg(filePath); |
|||
return ResponseEntity.ok("彩色图片已保存到: " + filePath); |
|||
} |
|||
} |
@ -0,0 +1,148 @@ |
|||
package com.iflytop.gd.hardware.drivers; |
|||
|
|||
import com.iflytop.gd.camera.BaslerCameraWrapper; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.opencv.core.CvType; |
|||
import org.opencv.core.Mat; |
|||
import org.opencv.imgcodecs.Imgcodecs; |
|||
import org.opencv.imgproc.Imgproc; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
public class CameraBaslerDriver { |
|||
// camera |
|||
private static BaslerCameraWrapper pylon = null; |
|||
// index |
|||
private Integer index = 0; |
|||
// channel |
|||
protected Integer channel = 0; |
|||
// camera handle |
|||
private long cam = -1; |
|||
|
|||
// 设置相机索引 |
|||
public void setIndex(Integer index) { |
|||
this.index = index; |
|||
log.info("设置相机索引为: {}", index); |
|||
} |
|||
|
|||
// get pylon |
|||
private BaslerCameraWrapper getPylon() { |
|||
if (null == CameraBaslerDriver.pylon) { |
|||
CameraBaslerDriver.pylon = new BaslerCameraWrapper(); |
|||
CameraBaslerDriver.pylon.initialize(); |
|||
log.info("Basler相机库初始化完成"); |
|||
} |
|||
return CameraBaslerDriver.pylon; |
|||
} |
|||
|
|||
// 使能相机 |
|||
public void enable() { |
|||
if (-1 != this.cam) { |
|||
log.info("相机已使能,句柄: {}", this.cam); |
|||
return; |
|||
} |
|||
try { |
|||
var pylon = this.getPylon(); |
|||
int count = pylon.enumerateDevices(); |
|||
if (this.index >= count) { |
|||
log.error("相机索引超出范围: {}", this.index); |
|||
throw new RuntimeException("相机索引超出范围: " + this.index); |
|||
} |
|||
this.cam = pylon.createDeviceByIndex(this.index); |
|||
pylon.deviceOpen(this.cam, BaslerCameraWrapper.ACCESS_MODE_CONTROL | BaslerCameraWrapper.ACCESS_MODE_STREAM); |
|||
|
|||
boolean isFeatureReadable = pylon.deviceFeatureIsReadable(cam, "DeviceModelName"); |
|||
if (isFeatureReadable) { |
|||
String name = pylon.deviceFeatureToString(cam, "DeviceModelName", 256); |
|||
log.info("相机型号: {}", name); |
|||
} |
|||
|
|||
// pylon.deviceFeatureFromString(cam, "PixelFormat", "Mono8"); |
|||
pylon.deviceFeatureFromString(cam, "PixelFormat", "BayerRG8"); |
|||
pylon.deviceFeatureFromString(cam, "TriggerSelector", "AcquisitionStart"); |
|||
pylon.deviceFeatureFromString(cam, "TriggerMode", "Off"); |
|||
pylon.deviceFeatureFromString(cam, "TriggerSelector", "FrameStart"); |
|||
pylon.deviceFeatureFromString(cam, "TriggerMode", "Off"); |
|||
pylon.deviceSetIntegerFeature(cam, "GevSCPSPacketSize", 1500); |
|||
pylon.deviceFeatureFromString(cam, "ExposureAuto", "Off"); |
|||
pylon.deviceSetIntegerFeature(cam, "ExposureTimeRaw", 27218); |
|||
|
|||
log.info("相机使能成功,句柄: {}", this.cam); |
|||
} catch (Exception e) { |
|||
log.error("相机使能失败: {}", e.getMessage(), e); |
|||
throw new RuntimeException("相机使能失败: " + e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
// 关闭相机 |
|||
public void disable() { |
|||
if (-1 == this.cam) { |
|||
log.info("相机未使能,无需关闭"); |
|||
return; |
|||
} |
|||
var pylon = this.getPylon(); |
|||
pylon.deviceClose(this.cam); |
|||
log.info("相机关闭,句柄: {}", this.cam); |
|||
this.cam = -1; |
|||
} |
|||
|
|||
// 抓取图像为Mat |
|||
public Mat grabToMat() { |
|||
if (this.cam == -1) { |
|||
log.error("抓图失败:相机未使能"); |
|||
throw new RuntimeException("抓图失败:相机未使能"); |
|||
} |
|||
var pylon = this.getPylon(); |
|||
var result = pylon.deviceGrabSingleFrame(this.cam, this.channel); |
|||
if (result == null || result.imageBuffer == null) { |
|||
log.error("抓图失败:未获取到图像数据"); |
|||
throw new RuntimeException("抓图失败:未获取到图像数据"); |
|||
} |
|||
Mat frameMat = new Mat(result.sizeY, result.sizeX, CvType.CV_8UC1); |
|||
frameMat.put(0, 0, result.imageBuffer); |
|||
log.info("抓图成功,返回Mat对象"); |
|||
return frameMat; |
|||
} |
|||
|
|||
// 抓图并保存图片 |
|||
public void saveImg(String filePath) { |
|||
if (this.cam == -1) { |
|||
log.error("保存图片失败:相机未使能"); |
|||
throw new RuntimeException("保存图片失败:相机未使能"); |
|||
} |
|||
var pylon = this.getPylon(); |
|||
var result = pylon.deviceGrabSingleFrame(this.cam, this.channel); |
|||
if (result == null || result.imageBuffer == null) { |
|||
log.error("保存图片失败:未获取到图像数据"); |
|||
throw new RuntimeException("保存图片失败:未获取到图像数据"); |
|||
} |
|||
Mat frameMat = new Mat(result.sizeY, result.sizeX, CvType.CV_8UC1); |
|||
frameMat.put(0, 0, result.imageBuffer); |
|||
Imgcodecs.imwrite(filePath, frameMat); |
|||
log.info("图片已保存到: {}", filePath); |
|||
} |
|||
|
|||
// 抓图并保存彩色图片 |
|||
public void saveColorImg(String filePath) { |
|||
if (this.cam == -1) { |
|||
log.error("保存彩色图片失败:相机未使能"); |
|||
throw new RuntimeException("保存彩色图片失败:相机未使能"); |
|||
} |
|||
var pylon = this.getPylon(); |
|||
var result = pylon.deviceGrabSingleFrame(this.cam, this.channel); |
|||
if (result == null || result.imageBuffer == null) { |
|||
log.error("保存彩色图片失败:未获取到图像数据"); |
|||
throw new RuntimeException("保存彩色图片失败:未获取到图像数据"); |
|||
} |
|||
// 假设 result.imageBuffer 为8位单通道灰度,实际彩色需根据SDK返回格式调整 |
|||
Mat frameMat = new Mat(result.sizeY, result.sizeX, CvType.CV_8UC1); |
|||
frameMat.put(0, 0, result.imageBuffer); |
|||
|
|||
Mat colorMat = new Mat(); |
|||
Imgproc.cvtColor(frameMat, colorMat, Imgproc.COLOR_BayerRG2RGB); |
|||
|
|||
Imgcodecs.imwrite(filePath, colorMat); |
|||
log.info("彩色图片已保存到: {}", filePath); |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue