Browse Source

feat:增加拍照相关接口并实现

master
白凤吉 2 months ago
parent
commit
cb3fb56d41
  1. 2
      build.gradle
  2. BIN
      lib/opencv-420.jar
  3. 4
      src/main/java/com/iflytop/gd/app/controller/PhotoController.java
  4. 46
      src/main/java/com/iflytop/gd/app/core/runner/BaslerCameraApplicationRunner.java
  5. 15
      src/main/java/com/iflytop/gd/app/service/api/PhotosService.java
  6. BIN
      src/main/java/com/iflytop/gd/camera/BaslerCameraWrapper$GrabResult.class
  7. BIN
      src/main/java/com/iflytop/gd/camera/BaslerCameraWrapper.class
  8. 130
      src/main/java/com/iflytop/gd/camera/BaslerCameraWrapper.java
  9. 65
      src/main/java/com/iflytop/gd/hardware/controller/BaslerCameraController.java
  10. 148
      src/main/java/com/iflytop/gd/hardware/drivers/CameraBaslerDriver.java
  11. 14
      src/main/resources/application-dev.yml

2
build.gradle

@ -41,7 +41,7 @@ dependencies {
implementation 'jakarta.annotation:jakarta.annotation-api:3.0.0' implementation 'jakarta.annotation:jakarta.annotation-api:3.0.0'
implementation 'com.fazecast:jSerialComm:2.11.0' implementation 'com.fazecast:jSerialComm:2.11.0'
implementation 'com.opencsv:opencsv:5.11' implementation 'com.opencsv:opencsv:5.11'
implementation files('lib/modbus4j-3.1.0.jar')
implementation fileTree(dir: 'lib', include: '*.jar')
compileOnly 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok'

BIN
lib/opencv-420.jar

4
src/main/java/com/iflytop/gd/app/controller/PhotoController.java

@ -8,6 +8,7 @@ import com.iflytop.gd.app.service.api.PhotosService;
import com.iflytop.gd.common.base.BasePageQuery; import com.iflytop.gd.common.base.BasePageQuery;
import com.iflytop.gd.common.result.PageResult; import com.iflytop.gd.common.result.PageResult;
import com.iflytop.gd.common.result.Result; import com.iflytop.gd.common.result.Result;
import com.iflytop.gd.hardware.drivers.CameraBaslerDriver;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -25,7 +26,6 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class PhotoController { public class PhotoController {
private final PhotosService photosService; private final PhotosService photosService;
@Operation(summary = "照片列表") @Operation(summary = "照片列表")
@ -40,7 +40,7 @@ public class PhotoController {
@Operation(summary = "拍摄一张照片") @Operation(summary = "拍摄一张照片")
@GetMapping("/take") @GetMapping("/take")
public Result<String> take() { public Result<String> take() {
return Result.success("http://127.0.0.1/photos/temp/1.png");
return Result.success(photosService.take());
} }

46
src/main/java/com/iflytop/gd/app/core/runner/BaslerCameraApplicationRunner.java

@ -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);
}
}
}

15
src/main/java/com/iflytop/gd/app/service/api/PhotosService.java

@ -3,7 +3,9 @@ package com.iflytop.gd.app.service.api;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iflytop.gd.app.mapper.PhotosMapper; import com.iflytop.gd.app.mapper.PhotosMapper;
import com.iflytop.gd.app.model.entity.Photos; import com.iflytop.gd.app.model.entity.Photos;
import com.iflytop.gd.hardware.drivers.CameraBaslerDriver;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
@ -12,6 +14,19 @@ import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class PhotosService extends ServiceImpl<PhotosMapper, Photos> { public class PhotosService extends ServiceImpl<PhotosMapper, Photos> {
private final CameraBaslerDriver driver;
@Value("${photo.url}")
private String url;
@Value("${photo.path}")
private String path;
public String take() {
String filePath = path + "/temp/" + System.currentTimeMillis() + ".png";
driver.enable();
driver.saveColorImg(filePath);
driver.disable();
return url + filePath;
}
} }

BIN
src/main/java/com/iflytop/gd/camera/BaslerCameraWrapper$GrabResult.class

BIN
src/main/java/com/iflytop/gd/camera/BaslerCameraWrapper.class

130
src/main/java/com/iflytop/gd/camera/BaslerCameraWrapper.java

@ -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);
}

65
src/main/java/com/iflytop/gd/hardware/controller/BaslerCameraController.java

@ -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);
}
}

148
src/main/java/com/iflytop/gd/hardware/drivers/CameraBaslerDriver.java

@ -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);
}
}

14
src/main/resources/application-dev.yml

@ -18,7 +18,7 @@ spring:
mybatis-plus: mybatis-plus:
configuration: configuration:
# 开启 SQL 日志输出(可选) # 开启 SQL 日志输出(可选)
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 如果需要加载 XML 文件(自定义 SQL),可配置 mapper-locations: # 如果需要加载 XML 文件(自定义 SQL),可配置 mapper-locations:
mapper-locations: classpath*:mapper/*.xml mapper-locations: classpath*:mapper/*.xml
@ -49,3 +49,15 @@ iflytophald:
modbus: modbus:
port: ttyS1 port: ttyS1
baudrate: 9600 baudrate: 9600
opencv:
library-path: /usr/lib/jni/libopencv_java420.so
pylon:
library-path: /opt/pylon-8.1.0_linux-aarch64/lib/libpylonc.so.10.0.0
wrapper-path: /opt/basler_camera/lib/libbaslercamera.so
photo:
url: http://192.168.8.168/static/photo
path: /home/firefly/package/photo
Loading…
Cancel
Save