Compare commits
2 Commits
32d8f2e5fb
...
6725634e8e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6725634e8e | ||
|
|
c7f9482883 |
@ -5,6 +5,7 @@ import com.zhangy.skyeye.common.extend.anno.IgnoreAuth;
|
|||||||
import com.zhangy.skyeye.common.extend.enums.EnumUtil;
|
import com.zhangy.skyeye.common.extend.enums.EnumUtil;
|
||||||
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
||||||
import com.zhangy.skyeye.common.extend.util.DateUtil;
|
import com.zhangy.skyeye.common.extend.util.DateUtil;
|
||||||
|
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
||||||
import com.zhangy.skyeye.common.pojo.result.Result;
|
import com.zhangy.skyeye.common.pojo.result.Result;
|
||||||
import com.zhangy.skyeye.jm.consts.JmJobModeEnum;
|
import com.zhangy.skyeye.jm.consts.JmJobModeEnum;
|
||||||
import com.zhangy.skyeye.jm.dto.JmJobDTO;
|
import com.zhangy.skyeye.jm.dto.JmJobDTO;
|
||||||
@ -77,6 +78,10 @@ public class JmJobController {
|
|||||||
@PostMapping("/save")
|
@PostMapping("/save")
|
||||||
public Object insert(@Valid @RequestBody JmJobDTO e) {
|
public Object insert(@Valid @RequestBody JmJobDTO e) {
|
||||||
JmJobModeEnum mode = EnumUtil.parseEx(JmJobModeEnum.class, e.getMode());
|
JmJobModeEnum mode = EnumUtil.parseEx(JmJobModeEnum.class, e.getMode());
|
||||||
|
if (mode != JmJobModeEnum.CRUISE && ObjectUtil.isEmpty(e.getPointList())) {
|
||||||
|
throw ServiceException.noLog("任务点不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
clearId(e);
|
clearId(e);
|
||||||
// 默认执行一次性任务
|
// 默认执行一次性任务
|
||||||
if (e.getType() == null) {
|
if (e.getType() == null) {
|
||||||
|
|||||||
@ -52,4 +52,14 @@ public class JmAirlineStatusDTO {
|
|||||||
|
|
||||||
// 前一张右上、右下
|
// 前一张右上、右下
|
||||||
private Double[] beforeRight;
|
private Double[] beforeRight;
|
||||||
|
|
||||||
|
/** 航线终点经度 */
|
||||||
|
private Double endLon;
|
||||||
|
|
||||||
|
/** 航线终点纬度 */
|
||||||
|
private Double endLat;
|
||||||
|
|
||||||
|
private Integer headingDiff;
|
||||||
|
|
||||||
|
private double[] values;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.zhangy.skyeye.jm.dto;
|
package com.zhangy.skyeye.jm.dto;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.zhangy.skyeye.jm.consts.JmJobModeEnum;
|
||||||
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
||||||
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
|
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -62,6 +63,9 @@ public class JmUavStatusDTO {
|
|||||||
/** sar 图片亮度 */
|
/** sar 图片亮度 */
|
||||||
private volatile Integer sarImageLight;
|
private volatile Integer sarImageLight;
|
||||||
|
|
||||||
|
/** 任务模式 */
|
||||||
|
private JmJobModeEnum jobMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取sar当前状态
|
* 获取sar当前状态
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.zhangy.skyeye.jm.entity;
|
package com.zhangy.skyeye.jm.entity;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.zhangy.skyeye.publics.dto.GeoTiffDTO;
|
import com.zhangy.skyeye.publics.dto.GeoTiffDTO;
|
||||||
import com.zhangy.skyeye.py.dto.PyImageParamDTO;
|
import com.zhangy.skyeye.py.dto.PyImageParamDTO;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -21,6 +22,9 @@ public class JmImage extends GeoTiffDTO {
|
|||||||
/** 任务执行ID */
|
/** 任务执行ID */
|
||||||
private Long jobId;
|
private Long jobId;
|
||||||
|
|
||||||
|
/** 任务配置ID */
|
||||||
|
private Long jobConfId;
|
||||||
|
|
||||||
/** 无人机 */
|
/** 无人机 */
|
||||||
private Long uavId;
|
private Long uavId;
|
||||||
|
|
||||||
@ -39,9 +43,22 @@ public class JmImage extends GeoTiffDTO {
|
|||||||
/** 归一化前最大值 */
|
/** 归一化前最大值 */
|
||||||
private Float max;
|
private Float max;
|
||||||
|
|
||||||
/** 航线的图片序号 */
|
/** 层级 */
|
||||||
|
private Integer tailLevel;
|
||||||
|
|
||||||
|
/** 图片序号 */
|
||||||
private Integer imageNo;
|
private Integer imageNo;
|
||||||
|
|
||||||
|
/** 起始帧 */
|
||||||
|
private Integer frameFrom;
|
||||||
|
|
||||||
|
/** 结束帧 */
|
||||||
|
private Integer frameTo;
|
||||||
|
|
||||||
|
/** 旋转角 */
|
||||||
|
private Double rotateAngle;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
非数据库字段
|
非数据库字段
|
||||||
*/
|
*/
|
||||||
@ -85,7 +102,7 @@ public class JmImage extends GeoTiffDTO {
|
|||||||
/** 图像亮度 */
|
/** 图像亮度 */
|
||||||
private Integer brightness;
|
private Integer brightness;
|
||||||
|
|
||||||
/** 图像识别结果 */
|
/** 识别结果 */
|
||||||
private List<JmImageItem> itemList;
|
private List<JmImageItem> itemList;
|
||||||
|
|
||||||
/** 转图像识别算法参数 */
|
/** 转图像识别算法参数 */
|
||||||
|
|||||||
@ -45,6 +45,15 @@ public interface JmImageMapper {
|
|||||||
*/
|
*/
|
||||||
List<JmImage> selectByAirline(@Param("type") Integer fileType, @Param("array") Long... airlineExecId);
|
List<JmImage> selectByAirline(@Param("type") Integer fileType, @Param("array") Long... airlineExecId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询航线最后一张图
|
||||||
|
*
|
||||||
|
* @param fileType 航线执行ID,必需
|
||||||
|
* @param airlineExecId 文件类型,非必需
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
JmImage selectLastByAirline(@Param("type") Integer fileType, @Param("array") Long... airlineExecId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按主键查询
|
* 按主键查询
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -41,6 +41,11 @@ public interface JmImageService {
|
|||||||
*/
|
*/
|
||||||
List<JmImage> selectLowByAirline(Long airlineExecId);
|
List<JmImage> selectLowByAirline(Long airlineExecId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询航线最后一张图
|
||||||
|
*/
|
||||||
|
JmImage selectLastByAirline(FileTypeEnum fileType, Long airlineExecId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按主键查询详情
|
* 按主键查询详情
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -119,6 +119,11 @@ public class JmImageServiceImpl implements JmImageService {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JmImage selectLastByAirline(FileTypeEnum fileType, Long airlineExecId) {
|
||||||
|
return imageMapper.selectLastByAirline(fileType.getValue(), airlineExecId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<JmImage> selectById(Long... id) {
|
public List<JmImage> selectById(Long... id) {
|
||||||
return imageMapper.selectById(id);
|
return imageMapper.selectById(id);
|
||||||
|
|||||||
@ -67,4 +67,16 @@ public class GeoTiffDTO implements Serializable {
|
|||||||
right2Lon = right1Lon;
|
right2Lon = right1Lon;
|
||||||
right2Lat = left2Lat;
|
right2Lat = left2Lat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateCoord(double left1Lon, double left1Lat, double left2Lon, double left2Lat,
|
||||||
|
double right1Lon, double right1Lat, double right2Lon, double right2Lat) {
|
||||||
|
this.left1Lon = left1Lon;
|
||||||
|
this.left1Lat = left1Lat;
|
||||||
|
this.left2Lon = left2Lon;
|
||||||
|
this.left2Lat = left2Lat;
|
||||||
|
this.right1Lon = right1Lon;
|
||||||
|
this.right1Lat = right1Lat;
|
||||||
|
this.right2Lon = right2Lon;
|
||||||
|
this.right2Lat = right2Lat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import java.nio.ByteOrder;
|
|||||||
public class ChecksumUtil {
|
public class ChecksumUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算校验和(表8、2),排除最后8个字节
|
* 表4,排除最后8个字节
|
||||||
* @param data
|
* @param data
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static int checksum8(byte[] data) {
|
public static int checksum32(byte[] data) {
|
||||||
long checksum = 0;
|
long checksum = 0;
|
||||||
ByteBuffer checksumBuffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
|
ByteBuffer checksumBuffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
int u32Count = data.length / 4;
|
int u32Count = data.length / 4;
|
||||||
@ -23,6 +23,21 @@ public class ChecksumUtil {
|
|||||||
return (int) (checksum & 0xFFFFFFFFL);
|
return (int) (checksum & 0xFFFFFFFFL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表14,排除最后8个字节
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int checksum8(byte[] data) {
|
||||||
|
int checksum = 0;
|
||||||
|
ByteBuffer checksumBuffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
int u32Count = data.length;
|
||||||
|
for (int i = 0; i < u32Count - 8; i++) { // 不检查校验和、帧尾
|
||||||
|
checksum += (Byte.toUnsignedInt(checksumBuffer.get()));
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断校验和(表9),排除第13字节
|
* 判断校验和(表9),排除第13字节
|
||||||
* @param data 如果分包数据不足1472,有效长度后面的是前一个分包留下的数据,并非是0
|
* @param data 如果分包数据不足1472,有效长度后面的是前一个分包留下的数据,并非是0
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
|||||||
import com.zhangy.skyeye.common.extend.util.FileUtil;
|
import com.zhangy.skyeye.common.extend.util.FileUtil;
|
||||||
import com.zhangy.skyeye.jm.dto.JmImageRotateDTO;
|
import com.zhangy.skyeye.jm.dto.JmImageRotateDTO;
|
||||||
import com.zhangy.skyeye.publics.dto.GeoTiffDTO;
|
import com.zhangy.skyeye.publics.dto.GeoTiffDTO;
|
||||||
|
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.geotools.api.geometry.Bounds;
|
import org.geotools.api.geometry.Bounds;
|
||||||
import org.geotools.coverage.grid.GridCoverage2D;
|
import org.geotools.coverage.grid.GridCoverage2D;
|
||||||
@ -38,15 +39,18 @@ public class ImageUtil {
|
|||||||
public static Mat afterCreate(Mat image, JmImageRotateDTO rotateDTO) {
|
public static Mat afterCreate(Mat image, JmImageRotateDTO rotateDTO) {
|
||||||
if (rotateDTO != null) {
|
if (rotateDTO != null) {
|
||||||
OpenCVUtil.flip(image, rotateDTO.getType());
|
OpenCVUtil.flip(image, rotateDTO.getType());
|
||||||
Mat rgbaImage = OpenCVUtil.rotate(image, rotateDTO.getAngle() * -1);
|
double angle = rotateDTO.getAngle();
|
||||||
|
if (angle != 0) {
|
||||||
|
Mat rgbaImage = OpenCVUtil.rotate(image, angle * -1);
|
||||||
image.release();
|
image.release();
|
||||||
image = rgbaImage;
|
image = rgbaImage;
|
||||||
}
|
}
|
||||||
int lightRate = rotateDTO.getLightRate();
|
|
||||||
if (lightRate != 0) {
|
|
||||||
//OpenCVUtil.multiply(image, lightRate);
|
|
||||||
OpenCVUtil.enhanceContrast(image, lightRate, 0);
|
|
||||||
}
|
}
|
||||||
|
// int lightRate = rotateDTO.getLightRate();
|
||||||
|
// if (lightRate != 0) {
|
||||||
|
// //OpenCVUtil.multiply(image, lightRate);
|
||||||
|
// OpenCVUtil.enhanceContrast(image, lightRate, 0);
|
||||||
|
// }
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +73,39 @@ public class ImageUtil {
|
|||||||
return OpenCVUtil.write(outPath, image, true);
|
return OpenCVUtil.write(outPath, image, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载回传图像,先转置再转为Mat
|
||||||
|
*
|
||||||
|
* @param frameData 图像帧数据,包含参数信息和未转置的8位灰度图像数据
|
||||||
|
* @param imageFrame 图像帧对象
|
||||||
|
* @return 转置后的4通道图像Mat
|
||||||
|
*/
|
||||||
|
public static Mat loadCurrImageMat(byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
||||||
|
Mat currImage = null;
|
||||||
|
int offset = imageFrame.IMAGE_OFFSET;
|
||||||
|
|
||||||
|
switch (imageFrame.getImgType()) {
|
||||||
|
case 0: // tif
|
||||||
|
currImage = ImageUtil.createImage(frameData, offset, imageFrame.getWidth(),
|
||||||
|
imageFrame.getHeight(), imageFrame.getImageBitDeep());
|
||||||
|
break;
|
||||||
|
case 1: // jpg,先去掉参数信息,只保留图像数据
|
||||||
|
case 4: { // png
|
||||||
|
byte[] validData = new byte[frameData.length - offset];
|
||||||
|
System.arraycopy(frameData, offset, validData, 0, validData.length);
|
||||||
|
currImage = ImageUtil.createImageFromJpg(validData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.error("不支持的图片类型imgType=" + imageFrame.getImgType() + ",丢弃图片");
|
||||||
|
}
|
||||||
|
if (currImage != null && currImage.empty()) {
|
||||||
|
log.error("图片数据是空的,数据长度:" + frameData.length);
|
||||||
|
currImage.release();
|
||||||
|
}
|
||||||
|
return currImage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建图像对象
|
* 创建图像对象
|
||||||
* @param rawData 原始像素数据
|
* @param rawData 原始像素数据
|
||||||
@ -213,12 +250,11 @@ public class ImageUtil {
|
|||||||
* @param currGray 新接收的单通道灰度图(右部分),会被释放资源
|
* @param currGray 新接收的单通道灰度图(右部分),会被释放资源
|
||||||
* @return 拼接后的4通道Mat矩阵
|
* @return 拼接后的4通道Mat矩阵
|
||||||
*/
|
*/
|
||||||
public static Mat join(Mat baseMat, Integer baseNo, Mat currGray, int currNo) {
|
public static Mat join(Mat baseMat, Integer baseNo, Mat currGray, int currNo, boolean isFirst) {
|
||||||
int width = currGray.cols();
|
int width = currGray.cols();
|
||||||
|
|
||||||
// 将当前灰度图转换为4通道(BGRA),支持16位
|
// 将当前灰度图转换为4通道(BGRA),支持16位
|
||||||
Mat curr4 = convertToRGBA(currGray);
|
Mat curr4 = convertToRGBA(currGray);
|
||||||
if (baseNo == null || baseMat == null) {
|
if (baseNo == null || baseMat == null || baseMat.empty()) { // 首图无需拼接,直接返回
|
||||||
return curr4;
|
return curr4;
|
||||||
}
|
}
|
||||||
// 统一数据类型:如果基准图是8位而当前是16位,需要转换
|
// 统一数据类型:如果基准图是8位而当前是16位,需要转换
|
||||||
@ -241,14 +277,13 @@ public class ImageUtil {
|
|||||||
Mat result = null;
|
Mat result = null;
|
||||||
// 检查帧号是否连续
|
// 检查帧号是否连续
|
||||||
int missingFrames = currNo - baseNo - 1;
|
int missingFrames = currNo - baseNo - 1;
|
||||||
if (missingFrames > 0) {
|
log.info("--------> 帧" + currNo +"开始拼接,填充" + missingFrames + ",基准图:" + baseMat.rows() + "," + baseMat.type() + "," + baseMat.dims() +
|
||||||
|
";当前图:" + curr4.rows() + "," + curr4.type() + "," + curr4.dims());
|
||||||
|
if (!isFirst && missingFrames > 0) {
|
||||||
// 创建缺失的透明图像(4通道)
|
// 创建缺失的透明图像(4通道)
|
||||||
Mat transparent = new Mat(baseHeight, width, baseMat.type(), new Scalar(0, 0, 0, 0));
|
Mat transparent = new Mat(baseHeight, width, baseMat.type(), new Scalar(0, 0, 0, 0));
|
||||||
|
|
||||||
// 准备要拼接的图像列表
|
// 准备要拼接的图像列表
|
||||||
log.info("当前帧" + currNo +"准备拼接图片,基准图:" + baseMat.rows() + "," + baseMat.type() + "," + baseMat.dims() +
|
|
||||||
";填充图:" + transparent.rows() + "," + transparent.type() + "," + transparent.dims() +
|
|
||||||
";当前图:" + curr4.rows() + "," + curr4.type() + "," + curr4.dims());
|
|
||||||
List<Mat> imagesToConcat = new ArrayList<>();
|
List<Mat> imagesToConcat = new ArrayList<>();
|
||||||
imagesToConcat.add(baseMat);
|
imagesToConcat.add(baseMat);
|
||||||
|
|
||||||
@ -272,12 +307,10 @@ public class ImageUtil {
|
|||||||
imagesToConcat.add(curr4);
|
imagesToConcat.add(curr4);
|
||||||
|
|
||||||
result = new Mat();
|
result = new Mat();
|
||||||
log.info("当前帧" + currNo +"准备拼接图片,基准图:" + baseMat.rows() + "," + baseMat.type() + "," + baseMat.dims() +
|
|
||||||
";当前图:" + curr4.rows() + "," + curr4.type() + "," + curr4.dims());
|
|
||||||
Core.hconcat(imagesToConcat, result);
|
Core.hconcat(imagesToConcat, result);
|
||||||
baseMat.release();
|
baseMat.release();
|
||||||
}
|
}
|
||||||
log.info("当前帧" + currNo +"拼接完成");
|
log.info("<-------- 帧" + currNo +"拼接完成");
|
||||||
// 释放资源,若missingFrames < 0 则说明当前图是前面的图迟到了,无法拼接,按丢图处理
|
// 释放资源,若missingFrames < 0 则说明当前图是前面的图迟到了,无法拼接,按丢图处理
|
||||||
curr4.release();
|
curr4.release();
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.zhangy.skyeye.sar.dto;
|
package com.zhangy.skyeye.sar.dto;
|
||||||
|
|
||||||
|
import com.zhangy.skyeye.publics.utils.ChecksumUtil;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -26,8 +27,12 @@ public class SarBackFramePackDTO {
|
|||||||
/** 帧尾标志 0x7EFFDC02 */
|
/** 帧尾标志 0x7EFFDC02 */
|
||||||
private int tailFlag;
|
private int tailFlag;
|
||||||
|
|
||||||
|
public SarBackFramePackDTO() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public boolean check() {
|
public boolean check() {
|
||||||
return headFlag == 0x7EFFDC01;
|
return headFlag == 0x7EFFDC01 && tailFlag == 0x7EFFDC02;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,24 +62,32 @@ public class SarBackFramePackDTO {
|
|||||||
* @param data 实际接收的帧数据
|
* @param data 实际接收的帧数据
|
||||||
* @param hasLastPacket 是否包含尾包
|
* @param hasLastPacket 是否包含尾包
|
||||||
*/
|
*/
|
||||||
public SarBackFramePackDTO(byte[] data, boolean hasLastPacket) {
|
public static SarBackFramePackDTO parse(byte[] data, boolean hasLastPacket) {
|
||||||
|
SarBackFramePackDTO dto = new SarBackFramePackDTO();
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
// 首包包含的头校验信息
|
// 首包包含的头校验信息
|
||||||
this.headFlag = buffer.getInt();
|
dto.headFlag = buffer.getInt();
|
||||||
this.size = buffer.getInt();
|
if (dto.headFlag != 0x7EFFDC01) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
dto.size = buffer.getInt();
|
||||||
|
|
||||||
// 图像数据 = 帧数据 - 头尾校验数据
|
// 图像数据 = 帧数据 - 头尾校验数据
|
||||||
int frameSize = data.length - 8 - (hasLastPacket ? 8 : 0);
|
int frameSize = data.length - 8 - (hasLastPacket ? 8 : 0);
|
||||||
this.frameData = new byte[frameSize];
|
dto.frameData = new byte[frameSize];
|
||||||
buffer.get(this.frameData, 0, frameSize);
|
buffer.get(dto.frameData, 0, frameSize);
|
||||||
|
|
||||||
// 尾包包含的尾校验信息
|
// 尾包包含的尾校验信息
|
||||||
if (hasLastPacket) {
|
if (hasLastPacket) {
|
||||||
this.checksum = buffer.getInt();
|
dto.checksum = (int) (buffer.getInt() & 0xFFFFFFFFL);
|
||||||
this.tailFlag = buffer.getInt();
|
dto.tailFlag = buffer.getInt();
|
||||||
|
if (dto.tailFlag != 0x7EFFDC02 || ChecksumUtil.checksum8(data) != dto.checksum) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@ -50,7 +50,7 @@ public class SarControlPackDTO {
|
|||||||
buffer.put(sarControl.toBytes());
|
buffer.put(sarControl.toBytes());
|
||||||
// 尾
|
// 尾
|
||||||
byte[] checkData = buffer.array();
|
byte[] checkData = buffer.array();
|
||||||
this.checksum = ChecksumUtil.checksum8(checkData);
|
this.checksum = ChecksumUtil.checksum32(checkData);
|
||||||
buffer.putInt(checksum);
|
buffer.putInt(checksum);
|
||||||
buffer.putInt(endFlag);
|
buffer.putInt(endFlag);
|
||||||
return buffer.array();
|
return buffer.array();
|
||||||
|
|||||||
@ -15,32 +15,32 @@ import java.util.zip.CRC32;
|
|||||||
public class SarImagePacketDTO extends SarBackPacketDTO {
|
public class SarImagePacketDTO extends SarBackPacketDTO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图像帧ID,随机不连续 uint32_t
|
* 图像帧ID,随机不连续
|
||||||
*/
|
*/
|
||||||
long sessionId;
|
long sessionId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 帧序号,从0开始 uint32_t
|
* 帧序号,从0开始
|
||||||
*/
|
*/
|
||||||
int packetNo;
|
int packetNo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 帧数据大小(字节) uint32_t
|
* 帧数据大小(字节)
|
||||||
*/
|
*/
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否为最后一个包 uint8_t
|
* 是否为最后一个包
|
||||||
*/
|
*/
|
||||||
boolean isLast;
|
boolean isLast;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验值 uint32_t
|
* 校验值
|
||||||
*/
|
*/
|
||||||
long crc32;
|
long crc32;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包数据 std::vector<char>
|
* 包数据
|
||||||
*/
|
*/
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
@ -55,12 +55,11 @@ public class SarImagePacketDTO extends SarBackPacketDTO {
|
|||||||
bb.order(ByteOrder.BIG_ENDIAN); // 网络字节序
|
bb.order(ByteOrder.BIG_ENDIAN); // 网络字节序
|
||||||
|
|
||||||
// 解析数据包头部
|
// 解析数据包头部
|
||||||
pkt.sessionId = bb.getInt() & 0xFFFFFFFFL; // uint32_t
|
pkt.sessionId = bb.getInt() & 0xFFFFFFFFL;
|
||||||
pkt.packetNo = bb.getInt(); // uint32_t
|
pkt.packetNo = bb.getInt();
|
||||||
pkt.size = bb.getInt(); // uint32_t
|
pkt.size = bb.getInt();
|
||||||
pkt.isLast = bb.get() != 0; // uint8_t -> boolean
|
pkt.isLast = bb.get() != 0;
|
||||||
pkt.crc32 = bb.getInt() & 0xFFFFFFFFL; // uint32_t
|
pkt.crc32 = bb.getInt() & 0xFFFFFFFFL;
|
||||||
|
|
||||||
// 提取数据部分
|
// 提取数据部分
|
||||||
byte[] pktData = new byte[pkt.size];
|
byte[] pktData = new byte[pkt.size];
|
||||||
bb.get(pktData);
|
bb.get(pktData);
|
||||||
@ -68,10 +67,9 @@ public class SarImagePacketDTO extends SarBackPacketDTO {
|
|||||||
// CRC校验
|
// CRC校验
|
||||||
long crcCalc = calculateCrc32(pktData);
|
long crcCalc = calculateCrc32(pktData);
|
||||||
if (pkt.crc32 != crcCalc) {
|
if (pkt.crc32 != crcCalc) {
|
||||||
log.warn("CRC不匹配,序号=" + pkt.packetNo + ",丢弃");
|
log.warn("CRC不匹配,序号=" + pkt.packetNo + ",丢弃包");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建Packet对象
|
// 创建Packet对象
|
||||||
pkt.data = pktData;
|
pkt.data = pktData;
|
||||||
return pkt;
|
return pkt;
|
||||||
|
|||||||
@ -31,6 +31,9 @@ public class SarImagePacketGroupDTO extends SarBackPacketGroupDTO<SarImagePacket
|
|||||||
/** 分包数据 */
|
/** 分包数据 */
|
||||||
private Map<Integer, SarImagePacketDTO> packets = new HashMap<>();
|
private Map<Integer, SarImagePacketDTO> packets = new HashMap<>();
|
||||||
|
|
||||||
|
/** 整包数据 */
|
||||||
|
private byte[] groupData;
|
||||||
|
|
||||||
public SarImagePacketDTO getLastPacket() {
|
public SarImagePacketDTO getLastPacket() {
|
||||||
if (packets.size() == 0) {
|
if (packets.size() == 0) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -0,0 +1,152 @@
|
|||||||
|
package com.zhangy.skyeye.sar.listen;
|
||||||
|
|
||||||
|
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
||||||
|
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
||||||
|
|
||||||
|
import com.zhangy.skyeye.sar.dto.SarImagePacketDTO;
|
||||||
|
import com.zhangy.skyeye.sar.dto.SarImagePacketGroupDTO;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收udp和处理异步进行,可靠传输
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public abstract class SarAsynAbstractUdpProcessor extends SarAbstractUdpProcessor<SarImagePacketGroupDTO, SarImagePacketDTO> {
|
||||||
|
|
||||||
|
// 任务队列容量
|
||||||
|
private final int QUEUE_CAPACITY = 1000;
|
||||||
|
|
||||||
|
// 任务队列
|
||||||
|
private BlockingQueue<SarImagePacketGroupDTO> packetQueue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
|
||||||
|
|
||||||
|
// 异步线程
|
||||||
|
private Thread processThread = new Thread(this::startProcessing);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JmJobStatusService jobStatusService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
public SarAsynAbstractUdpProcessor() {
|
||||||
|
super();
|
||||||
|
running = true;
|
||||||
|
processThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步处理
|
||||||
|
* @param group
|
||||||
|
*/
|
||||||
|
protected abstract void afterJoin(SarImagePacketGroupDTO group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动处理线程从队列消费数据
|
||||||
|
*/
|
||||||
|
private void startProcessing() {
|
||||||
|
while (running) {
|
||||||
|
try {
|
||||||
|
SarImagePacketGroupDTO group = packetQueue.poll(100, TimeUnit.MILLISECONDS);
|
||||||
|
if (group != null) {
|
||||||
|
try {
|
||||||
|
afterJoin(group);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error(getText() + "异步线程处理图像包错误:" + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.error(getText() + "异步线程处理异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以合并回图帧,首包不能丢
|
||||||
|
* @param group
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean canJoin(SarImagePacketGroupDTO group) {
|
||||||
|
Map<Integer, SarImagePacketDTO> packets = group.getPackets();
|
||||||
|
//log.info("定时判断合并udp:" + group.getSessionId() + "," + packets.keySet());
|
||||||
|
// 首包包含图片格式信息,不能丢,否则图片无法打开
|
||||||
|
return packets.get(0) != null && group.getLastPacket().isLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void expireProcess(SarImagePacketGroupDTO group) {
|
||||||
|
// 定时判断:超过 PACKET_TIMEOUT 时间未收到新包且符合生成条件则合成图
|
||||||
|
if (canJoin(group)) {
|
||||||
|
log.info("[UDP] sesionid="+ group.getSessionId() + "超时合并!");
|
||||||
|
join(group);
|
||||||
|
this.packetQueue.offer(group);
|
||||||
|
} else { // 超时且无法合并,丢弃
|
||||||
|
Map<Integer, SarImagePacketDTO> packets = group.getPackets();
|
||||||
|
if (packets.size() == 1 && packets.values().iterator().next().isLast()) {
|
||||||
|
// 雷达传图有bug,每帧的尾包会多发一个,丢弃且不打印日志
|
||||||
|
} else {
|
||||||
|
log.warn(getText() + "-移除超时帧: {}", group.getSessionId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将upd分包数据合并为完整帧(帧数据 + 头尾校验),因为无法确定分包大小,分包缺失无法补充0
|
||||||
|
* 合并后删除frameMap的分包数据
|
||||||
|
* 使用ByteBuffer替代ByteArrayOutputStream,以减少内存分配。ByteArrayOutputStream 的 toByteArray()底层用Arrays.copyOf实现,
|
||||||
|
* 有性能开销;ByteBuffer的array()无需拷贝数组
|
||||||
|
* @param group
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private void join(SarImagePacketGroupDTO group) {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(group.getTotalSize());
|
||||||
|
buffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
Map<Integer, SarImagePacketDTO> packets = group.getPackets();
|
||||||
|
for (int i = 0; i <= group.getLastPacket().getPacketNo(); i++) {
|
||||||
|
SarImagePacketDTO packet = packets.get(i);
|
||||||
|
if (packet != null) {
|
||||||
|
buffer.put(packet.getData(), 0, packet.getSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.setGroupData(buffer.array());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SarImagePacketGroupDTO putPacket(String sourceIp, SarImagePacketDTO packet) {
|
||||||
|
// 1.包放入缓冲区
|
||||||
|
long sessionId = packet.getSessionId();
|
||||||
|
SarImagePacketGroupDTO group;
|
||||||
|
if (contains(sourceIp, sessionId)) {
|
||||||
|
group = get(sourceIp, sessionId);
|
||||||
|
group.put(packet);
|
||||||
|
} else {
|
||||||
|
group = SarImagePacketGroupDTO.init(sourceIp, packet);
|
||||||
|
// 将帧与航线绑定,当航线2开机但航线1的图片未传完时,用航线ID判断生成的图片是属于哪条航线
|
||||||
|
JmAirlineStatusDTO currAirline = jobStatusService.getCurrAirline(sourceIp);
|
||||||
|
if (currAirline == null) {
|
||||||
|
return null; // 没有在执行的任务或航线
|
||||||
|
}
|
||||||
|
group.setAirlineExecId(currAirline.getExecId());
|
||||||
|
put(sourceIp, sessionId, group);
|
||||||
|
}
|
||||||
|
//System.out.println(packet);
|
||||||
|
// 2.判断是否全部包到达,是则合并
|
||||||
|
SarImagePacketDTO last = group.getLastPacket();
|
||||||
|
// 若所有包已收到则合并
|
||||||
|
if (last != null && last.isLast() && group.getPackets().size() == group.getMaxNo() + 1) {
|
||||||
|
log.info("[UDP] sesionid="+ packet.getSessionId() + "全部到达合并!");
|
||||||
|
join(group);
|
||||||
|
this.packetQueue.offer(group);
|
||||||
|
remove(sourceIp, sessionId);
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package com.zhangy.skyeye.sar.listen;
|
|||||||
|
|
||||||
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
||||||
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
||||||
|
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmImage;
|
import com.zhangy.skyeye.jm.entity.JmImage;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
||||||
import com.zhangy.skyeye.sar.dto.SarBackFramePackDTO;
|
import com.zhangy.skyeye.sar.dto.SarBackFramePackDTO;
|
||||||
@ -9,8 +10,10 @@ import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
|||||||
import com.zhangy.skyeye.sar.dto.SarImagePacketDTO;
|
import com.zhangy.skyeye.sar.dto.SarImagePacketDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarImagePacketGroupDTO;
|
import com.zhangy.skyeye.sar.dto.SarImagePacketGroupDTO;
|
||||||
import com.zhangy.skyeye.sar.service.ISarImageService;
|
import com.zhangy.skyeye.sar.service.ISarImageService;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@ -24,7 +27,7 @@ import java.util.Map;
|
|||||||
@Primary
|
@Primary
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class SarImageUdpProcessor extends SarAbstractUdpProcessor<SarImagePacketGroupDTO, SarImagePacketDTO> {
|
public class SarImageUdpProcessor extends SarAsynAbstractUdpProcessor {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISarImageService sarImageService;
|
private ISarImageService sarImageService;
|
||||||
@ -32,62 +35,52 @@ public class SarImageUdpProcessor extends SarAbstractUdpProcessor<SarImagePacket
|
|||||||
@Autowired
|
@Autowired
|
||||||
private JmJobStatusService jobStatusService;
|
private JmJobStatusService jobStatusService;
|
||||||
|
|
||||||
protected void afterJoin(byte[] framePacketData, SarImagePacketGroupDTO group) {
|
@Setter
|
||||||
|
@Value("${ld.sar.image.default-level:15}")
|
||||||
|
private Integer defaultTailLevel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterJoin(SarImagePacketGroupDTO group) {
|
||||||
boolean hasFirstPacket = group.getPackets().get(0) != null;// 如果有首包,则加头校验8字节
|
boolean hasFirstPacket = group.getPackets().get(0) != null;// 如果有首包,则加头校验8字节
|
||||||
boolean hasLastPacket = group.getLastPacket().isLast(); // 如果有尾包,则加尾校验8字节
|
boolean hasLastPacket = group.getLastPacket().isLast(); // 如果有尾包,则加尾校验8字节
|
||||||
SarBackFramePackDTO framePack = new SarBackFramePackDTO(framePacketData, hasLastPacket);
|
if (!hasLastPacket) {
|
||||||
byte[] frameData = framePack.getFrameData();
|
log.warn("帧(sessionId=" + group.getSessionId() + ")缺失尾包,丢弃帧");
|
||||||
SarBackImageFrameDTO imageFram = new SarBackImageFrameDTO(frameData);
|
}
|
||||||
if (!imageFram.check()) {
|
|
||||||
log.warn("图像帧 " + group.getSessionId() + " 已丢弃,因为图像信息不完整。数据:" + imageFram);
|
byte[] framePacketData = group.getGroupData();
|
||||||
|
SarBackFramePackDTO framePack = SarBackFramePackDTO.parse(framePacketData, hasLastPacket);
|
||||||
|
if (framePack == null) {
|
||||||
|
log.warn("图像帧 " + group.getSessionId() + " 已丢弃,帧数据校验失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.debug("图像帧数据:" + imageFram);
|
|
||||||
|
|
||||||
// 生成图片并更新数据库
|
byte[] frameData = framePack.getFrameData();
|
||||||
JmImage imageInfo = sarImageService.parseImage(group.getSourceIp(), group.getAirlineExecId(), frameData, imageFram);
|
SarBackImageFrameDTO frameDTO = new SarBackImageFrameDTO(frameData);
|
||||||
// 推送,仅当前有任务时,jobId 和 uavId 不能为空
|
if (!frameDTO.check()) {
|
||||||
if (imageInfo != null) {
|
log.warn("图像帧 " + group.getSessionId() + " 已丢弃,因为图像信息不完整。数据:" + frameDTO);
|
||||||
log.info("推送图片:" + JsonUtil.toString(imageInfo));
|
return;
|
||||||
sarBackWsService.sendImg(imageInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
log.debug("图像帧数据:" + frameDTO);
|
||||||
|
|
||||||
/**
|
// 1.取航线
|
||||||
* 判断是否可以合并回图帧,首包不能丢
|
Long airlineExecId = group.getAirlineExecId();
|
||||||
*
|
if (airlineExecId == null) {
|
||||||
* @param group
|
return;
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean canJoin(SarImagePacketGroupDTO group) {
|
|
||||||
Map<Integer, SarImagePacketDTO> packets = group.getPackets();
|
|
||||||
//log.info("定时判断合并udp:" + group.getSessionId() + "," + packets.keySet());
|
|
||||||
// 首包包含图片格式信息,不能丢,否则图片无法打开
|
|
||||||
return packets.get(0) != null;
|
|
||||||
}
|
}
|
||||||
|
JmUavStatusDTO uav = jobStatusService.getCurrUav(group.getSourceIp());
|
||||||
/**
|
if (uav == null) {
|
||||||
* 将upd分包数据合并为完整帧(帧数据 + 头尾校验),因为无法确定分包大小,分包缺失无法补充0
|
return;
|
||||||
* 合并后删除frameMap的分包数据
|
|
||||||
* 使用ByteBuffer替代ByteArrayOutputStream,以减少内存分配。ByteArrayOutputStream 的 toByteArray()底层用Arrays.copyOf实现,
|
|
||||||
* 有性能开销;ByteBuffer的array()无需拷贝数组
|
|
||||||
*
|
|
||||||
* @param group
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private byte[] join(SarImagePacketGroupDTO group) {
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(group.getTotalSize());
|
|
||||||
buffer.order(ByteOrder.BIG_ENDIAN);
|
|
||||||
|
|
||||||
Map<Integer, SarImagePacketDTO> packets = group.getPackets();
|
|
||||||
// 使用ByteBuffer的批量操作,减少系统调用
|
|
||||||
for (int i = 0; i <= group.getLastPacket().getPacketNo(); i++) {
|
|
||||||
SarImagePacketDTO packet = packets.get(i);
|
|
||||||
if (packet != null) {
|
|
||||||
buffer.put(packet.getData(), 0, packet.getSize());
|
|
||||||
}
|
}
|
||||||
|
// 查询图像对应的航线,不能查当前航线,因为可能是前一航线没传完的图
|
||||||
|
JmAirlineStatusDTO currAirline = uav.getAirline(airlineExecId);
|
||||||
|
if (currAirline == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 2.拼接图片
|
||||||
|
JmImage base = sarImageService.parseImage(uav, currAirline, airlineExecId, frameData, frameDTO);
|
||||||
|
if (base == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return buffer.array();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -95,58 +88,9 @@ public class SarImageUdpProcessor extends SarAbstractUdpProcessor<SarImagePacket
|
|||||||
return "雷达回图";
|
return "雷达回图";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SarImagePacketGroupDTO putPacket(String sourceIp, SarImagePacketDTO packet) {
|
|
||||||
// 1.包放入缓冲区
|
|
||||||
long sessionId = packet.getSessionId();
|
|
||||||
SarImagePacketGroupDTO group;
|
|
||||||
if (contains(sourceIp, sessionId)) {
|
|
||||||
group = get(sourceIp, sessionId);
|
|
||||||
group.put(packet);
|
|
||||||
} else {
|
|
||||||
group = SarImagePacketGroupDTO.init(sourceIp, packet);
|
|
||||||
// 将帧与航线绑定,当航线2开机但航线1的图片未传完时,用航线ID判断生成的图片是属于哪条航线
|
|
||||||
JmAirlineStatusDTO currAirline = jobStatusService.getCurrAirline(sourceIp);
|
|
||||||
if (currAirline == null) {
|
|
||||||
return null; // 没有在执行的任务或航线
|
|
||||||
}
|
|
||||||
group.setAirlineExecId(currAirline.getExecId());
|
|
||||||
put(sourceIp, sessionId, group);
|
|
||||||
}
|
|
||||||
// 2.判断是否全部包到达,是则合并
|
|
||||||
SarImagePacketDTO last = group.getLastPacket();
|
|
||||||
// 若所有包已收到则合并
|
|
||||||
if (last != null && last.isLast() && group.getPackets().size() == group.getMaxNo() + 1) {
|
|
||||||
log.info("[UDP] sesionid=" + packet.getSessionId() + "全部到达合并!");
|
|
||||||
byte[] framePacketData = join(group);
|
|
||||||
afterJoin(framePacketData, group);
|
|
||||||
remove(sourceIp, sessionId);
|
|
||||||
}
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getProcessorName() {
|
protected String getProcessorName() {
|
||||||
return "sarImageProcessor";
|
return "sarImageProcessor";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void expireProcess(SarImagePacketGroupDTO group) {
|
|
||||||
// 定时判断:超过 PACKET_TIMEOUT 时间未收到新包且符合生成条件则合成图
|
|
||||||
if (canJoin(group)) {
|
|
||||||
log.info("[UDP] sesionid: {} 超时合并!", group.getSessionId());
|
|
||||||
byte[] framePacketData = join(group);
|
|
||||||
afterJoin(framePacketData, group);
|
|
||||||
} else {
|
|
||||||
// 超时且无法合并,丢弃
|
|
||||||
Map<Integer, SarImagePacketDTO> packets = group.getPackets();
|
|
||||||
if (packets.size() == 1 && packets.values().iterator().next().isLast()) {
|
|
||||||
// 雷达传图有bug,每帧的尾包会多发一个,丢弃且不打印日志
|
|
||||||
} else {
|
|
||||||
log.warn("{}-移除超时帧: {}", getText(), group.getSessionId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package com.zhangy.skyeye.sar.service;
|
package com.zhangy.skyeye.sar.service;
|
||||||
|
|
||||||
|
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
||||||
|
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmImage;
|
import com.zhangy.skyeye.jm.entity.JmImage;
|
||||||
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
||||||
|
|
||||||
@ -15,5 +17,5 @@ public interface ISarImageService {
|
|||||||
* @param frameData 图像帧数据
|
* @param frameData 图像帧数据
|
||||||
* @param imageFrame 图像帧
|
* @param imageFrame 图像帧
|
||||||
*/
|
*/
|
||||||
JmImage parseImage(String sourceIp, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame);
|
JmImage parseImage(JmUavStatusDTO uav, JmAirlineStatusDTO currAirline, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ public class SarBackWsServiceImpl implements ISarBackWsService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendImg(JmImage imageInfo) {
|
public void sendImg(JmImage imageInfo) {
|
||||||
|
log.info("===send image back ======");
|
||||||
simpMessageingTemplate.convertAndSend(WebSocketKey.SAR_BACK_IMAGE, imageInfo);
|
simpMessageingTemplate.convertAndSend(WebSocketKey.SAR_BACK_IMAGE, imageInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.zhangy.skyeye.sar.service.impl;
|
package com.zhangy.skyeye.sar.service.impl;
|
||||||
|
|
||||||
import com.zhangy.skyeye.common.extend.util.MathUtil;
|
import com.zhangy.skyeye.common.extend.util.MathUtil;
|
||||||
|
import com.zhangy.skyeye.jm.consts.JmJobModeEnum;
|
||||||
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
||||||
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
||||||
@ -14,6 +15,7 @@ import com.zhangy.skyeye.publics.service.SysFileTypeService;
|
|||||||
import com.zhangy.skyeye.publics.utils.ImageUtil;
|
import com.zhangy.skyeye.publics.utils.ImageUtil;
|
||||||
import com.zhangy.skyeye.publics.utils.OpenCVUtil;
|
import com.zhangy.skyeye.publics.utils.OpenCVUtil;
|
||||||
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
||||||
|
import com.zhangy.skyeye.sar.service.ISarBackWsService;
|
||||||
import com.zhangy.skyeye.sar.service.ISarImageService;
|
import com.zhangy.skyeye.sar.service.ISarImageService;
|
||||||
import com.zhangy.skyeye.sar.service.SarWsAsyncService;
|
import com.zhangy.skyeye.sar.service.SarWsAsyncService;
|
||||||
import com.zhangy.skyeye.sar.util.RadarDisplayOptions;
|
import com.zhangy.skyeye.sar.util.RadarDisplayOptions;
|
||||||
@ -44,20 +46,15 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SarWsAsyncService sarWsAsyncService;
|
private SarWsAsyncService sarWsAsyncService;
|
||||||
|
@Autowired
|
||||||
|
protected ISarBackWsService sarBackWsService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
// 图片最大宽度,前端说和电脑有关,4096保险点,一般是4096 8192 16384
|
// 拼图数量
|
||||||
@Value("${skyeye.sar.image.max:4096}")
|
@Value("${skyeye.sar.image.count:5}")
|
||||||
@Setter
|
private long count;
|
||||||
private int IMG_MAX_WITH;
|
|
||||||
// 起始帧号
|
|
||||||
private final String CACHE_FIELD_START_FRAME_NO = "startFrameNo";
|
|
||||||
// 当前帧号
|
|
||||||
private final String CACHE_FIELD_CURR_FRAME_NO = "currFrameNo";
|
|
||||||
// 缓存超时(秒)
|
|
||||||
private final long CACHE_EXPIRE_SECOND = 24 * 3600;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取基准图像信息
|
* 获取基准图像信息
|
||||||
@ -65,91 +62,40 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
* @param airlineId 航线执行ID
|
* @param airlineId 航线执行ID
|
||||||
* @param singleWidth 单条图片宽度
|
* @param singleWidth 单条图片宽度
|
||||||
* @param frameNo 当前帧号
|
* @param frameNo 当前帧号
|
||||||
* @return 返回非空的图像信息,其字段 imageNo 一定有值
|
* @return 返回非空的图像信息
|
||||||
*/
|
*/
|
||||||
private JmImage getBaseImage(Long airlineId, int singleWidth, int frameNo) {IMG_MAX_WITH=1;
|
private JmImage getBaseImage(JmUavStatusDTO uav, Long airlineId, int singleWidth, int frameNo) {
|
||||||
List<JmImage> imageList = imageService.selectLowByAirline(airlineId);
|
JmImage last = imageService.selectLastByAirline(FileTypeEnum.SAR_IMAGE_LOW, airlineId);
|
||||||
|
|
||||||
|
// last = null;
|
||||||
|
|
||||||
String cacheKey = "jmImgJoin-" + airlineId;
|
String cacheKey = "jmImgJoin-" + airlineId;
|
||||||
JmImage base = null;
|
JmImage base = null;
|
||||||
// 情况1:航线第一张图
|
// 情况1:航线第一张图
|
||||||
if (ObjectUtil.isEmpty(imageList)) {
|
if (last == null) {
|
||||||
base = new JmImage();
|
base = new JmImage();
|
||||||
base.setImageNo(1);
|
base.setImageNo(1);
|
||||||
redisUtil.hset(cacheKey, CACHE_FIELD_START_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);
|
base.setFrameFrom(frameNo);
|
||||||
|
return base;
|
||||||
|
} else if (uav.getJobMode() == JmJobModeEnum.CRUISE) { // 手动模式不拼接
|
||||||
|
base = new JmImage();
|
||||||
|
base.setImageNo(last.getImageNo() + 1);
|
||||||
|
base.setFrameFrom(frameNo);
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
// 情况2:如果最后一张还能拼图,则直接返回继续拼
|
// 情况2:如果最后一张还能拼图,则直接返回继续拼
|
||||||
JmImage last = imageList.get(imageList.size() - 1);
|
File lastFile = new File(last.getFilePath());
|
||||||
Integer startFrameNo = (Integer) redisUtil.hget(cacheKey, CACHE_FIELD_START_FRAME_NO);
|
if (frameNo - last.getFrameFrom() < count) { // 当前图+填充 不能超过允许拼接数
|
||||||
int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1); // 图宽(当前图+基准图)
|
log.info("当前帧:" + last.getFrameFrom() + "," + frameNo + " 可以继续拼接");
|
||||||
int surplusNum = (IMG_MAX_WITH - currWidth) / singleWidth; // 还可以拼图片数
|
|
||||||
Integer baseNo = (Integer) redisUtil.hget("jmImgJoin-" + airlineId, CACHE_FIELD_CURR_FRAME_NO);
|
|
||||||
if (startFrameNo == null || currWidth < IMG_MAX_WITH ||
|
|
||||||
baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) { // 当前图+填充 不能超过允许拼接数
|
|
||||||
log.info("当前宽度:" + currWidth + " < " + IMG_MAX_WITH + " 可以继续拼接");
|
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
// 情况3:已经拼接到最大数量,或者当前图+填充数量超过允许拼接数量,创建新图像文件
|
// 情况3:已经拼接到最大数量,或者当前图+填充数量超过允许拼接数量,创建新图像文件
|
||||||
log.info("当前宽度:" + currWidth + " > " + IMG_MAX_WITH + " 重新拼接,当前帧号" + frameNo + "作为首帧");
|
log.info("当前帧:" + last.getFrameFrom() + "," + frameNo + " 重新拼接,当前帧号" + frameNo + "作为首帧");
|
||||||
base = new JmImage();
|
base = new JmImage();
|
||||||
redisUtil.hset(cacheKey, CACHE_FIELD_START_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);
|
|
||||||
base.setImageNo(last.getImageNo() + 1);
|
base.setImageNo(last.getImageNo() + 1);
|
||||||
|
base.setFrameFrom(frameNo);
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void modCoord(SarBackImageFrameDTO imageFrame, boolean lostImage, JmAirlineStatusDTO currAirline) {
|
|
||||||
JmImageRotateDTO rotateDTO = JmImageRotateDTO.rotate(currAirline.getTargetHeading(), currAirline.getDirection());
|
|
||||||
Double[] before = currAirline.getBeforeRight();
|
|
||||||
boolean isFirst = before == null;
|
|
||||||
if (isFirst) {
|
|
||||||
before = new Double[4];
|
|
||||||
currAirline.setBeforeRight(before);
|
|
||||||
}
|
|
||||||
// 使用前一张图的右侧坐标作为后一张图的左侧,前提是没丢图
|
|
||||||
if (!isFirst && !lostImage) {
|
|
||||||
/*imageFrame.setLon1(before[0]);
|
|
||||||
imageFrame.setLat1(before[1]);
|
|
||||||
imageFrame.setLon4(before[2]);
|
|
||||||
imageFrame.setLat4(before[3]);*/
|
|
||||||
}
|
|
||||||
before[0] = imageFrame.getLon5();
|
|
||||||
before[1] = imageFrame.getLat5();
|
|
||||||
before[2] = imageFrame.getLon8();
|
|
||||||
before[3] = imageFrame.getLat8();
|
|
||||||
/*switch (rotateDTO.getType()) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
// 使用前一张图的右侧坐标作为后一张图的左侧,前提是没丢图
|
|
||||||
if (!isFirst && !lostImage) {
|
|
||||||
imageFrame.setLon1(before[0]);
|
|
||||||
imageFrame.setLat1(before[1]);
|
|
||||||
imageFrame.setLon4(before[2]);
|
|
||||||
imageFrame.setLat4(before[3]);
|
|
||||||
} else if (before == null) {
|
|
||||||
before = new Double[4];
|
|
||||||
currAirline.setBeforeRight(before);
|
|
||||||
}
|
|
||||||
before[0] = imageFrame.getLon5();
|
|
||||||
before[1] = imageFrame.getLat5();
|
|
||||||
before[2] = imageFrame.getLon8();
|
|
||||||
before[3] = imageFrame.getLat8();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
if (!isFirst && !lostImage) {
|
|
||||||
imageFrame.setLon5(before[0]);
|
|
||||||
imageFrame.setLat5(before[1]);
|
|
||||||
imageFrame.setLon8(before[2]);
|
|
||||||
imageFrame.setLat8(before[3]);
|
|
||||||
}
|
|
||||||
before[0] = imageFrame.getLon1();
|
|
||||||
before[1] = imageFrame.getLat1();
|
|
||||||
before[2] = imageFrame.getLon4();
|
|
||||||
before[3] = imageFrame.getLat4();
|
|
||||||
break;
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将原始图像数据保存为dat文件,图像转png并将路径推送给前端
|
* 将原始图像数据保存为dat文件,图像转png并将路径推送给前端
|
||||||
*
|
*
|
||||||
@ -159,107 +105,59 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
* @param imageFrame 图像帧
|
* @param imageFrame 图像帧
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public JmImage parseImage(String sourceIp, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
public JmImage parseImage(JmUavStatusDTO uav, JmAirlineStatusDTO currAirline, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
||||||
if (airlineExecId == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int frameNo = imageFrame.getFrameNo();
|
int frameNo = imageFrame.getFrameNo();
|
||||||
JmUavStatusDTO uav = jobStatusService.getCurrUav(sourceIp);
|
|
||||||
// 若载荷任务结束,sar仍然回传图像则 uav为空。此场景仅限sardemo,实际sar不会发生
|
|
||||||
if (uav == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 查询图像对应的航线,不能查当前航线,因为可能是前一航线没传完的图
|
|
||||||
JmAirlineStatusDTO currAirline = uav.getAirline(airlineExecId);
|
|
||||||
if (currAirline == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Long jobExecId = uav.getJobExecId();
|
Long jobExecId = uav.getJobExecId();
|
||||||
//----------------------------------- 处理图像 --------------------------------------------------------
|
//----------------------------------- 处理图像 --------------------------------------------------------
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
// 1.保存dat(异步)
|
// 1.保存dat(异步)
|
||||||
sarWsAsyncService.saveImageDat(jobExecId, airlineExecId + "-" + frameNo + ".dat", frameData);
|
sarWsAsyncService.saveImageDat(jobExecId, airlineExecId + "-" + frameNo + ".dat", frameData);
|
||||||
|
|
||||||
// 2.生成当前图像矩阵
|
// 2.生成当前图像矩阵
|
||||||
Mat currImage = loadCurrImageMat(frameData, imageFrame);
|
Mat currImage = ImageUtil.loadCurrImageMat(frameData, imageFrame);
|
||||||
if (currImage == null) {
|
if (currImage == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.保存图像png,用航线ID+序号命名
|
// 3.保存图像png,用航线ID+序号命名
|
||||||
JmImage base = getBaseImage(airlineExecId, currImage.width(), imageFrame.getFrameNo());
|
JmImage base = getBaseImage(uav, airlineExecId, currImage.width(), imageFrame.getFrameNo());
|
||||||
String imageName = airlineExecId + "-" + base.getImageNo() +".png";
|
String baseName = airlineExecId + "-" + base.getImageNo() +"-base.png";
|
||||||
String[] imagePath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, imageName);
|
String[] basePath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, baseName);
|
||||||
String currPath = imagePath[0];
|
|
||||||
|
|
||||||
System.out.println("帧:" + frameNo);
|
// 4.保存基准图(同步),并拼接
|
||||||
|
Integer baseNo = base.getFrameTo();
|
||||||
|
|
||||||
// 4.保存基准图(同步),用于下次拼接
|
Mat baseMat = generateBaseMat(base, currImage, frameNo, imageFrame.getMax(), basePath[0], baseNo);
|
||||||
Integer baseNo = (Integer) redisUtil.hget("jmImgJoin-" + airlineExecId, CACHE_FIELD_CURR_FRAME_NO);
|
|
||||||
boolean lostImage = baseNo != null && (frameNo - baseNo > 1); // 判断是否丢图
|
|
||||||
String basePath = sysFileTypeService.getAbsolutePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId,
|
|
||||||
airlineExecId + "-" + base.getImageNo() +"-base.png");
|
|
||||||
Mat baseMat = generateBaseMat(base, currImage, frameNo, imageFrame.getMax(), basePath, baseNo);
|
|
||||||
if (baseMat == null) { // 拼接失败 或 基准图生成失败则跳过,按丢图处理
|
if (baseMat == null) { // 拼接失败 或 基准图生成失败则跳过,按丢图处理
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (lostImage) {
|
|
||||||
log.warn("丢图"+(frameNo - baseNo)+"张!当前帧" + frameNo + ",前帧" + baseNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
//modCoord(imageFrame, lostImage, currAirline);
|
// 更新坐标
|
||||||
redisUtil.hset("jmImgJoin-" + airlineExecId, CACHE_FIELD_CURR_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);// 更新帧号
|
boolean isFirst = base.getId() == null;
|
||||||
// ### 亮度调整,用于可靠udp版本图像,固定使用系数0.5
|
// ### 亮度调整,用于可靠udp版本图像,固定使用系数0.5 手动模式不调整
|
||||||
// 拆分多张图片,去掉自适应调整
|
// enhance image
|
||||||
if (IMG_MAX_WITH > 20000) {
|
// if (uav.getJobMode() != JmJobModeEnum.CRUISE) {
|
||||||
SarImageToneAdjuster.autoToneWithClipping(baseMat, 0.5f);
|
SarImageToneAdjuster.autoToneWithClipping(baseMat, 0.5f);
|
||||||
// RadarDisplayOptions options = new RadarDisplayOptions();
|
// }
|
||||||
// SarImageToneAdjuster.processForDisplayMat(baseMat, options);
|
|
||||||
// SarImageToneAdjuster.applyPseudoColorMat(baseMat, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5.保存后处理图(异步),用于前端显示
|
// 5.保存后处理图(异步),用于前端显示
|
||||||
generateAfterMat(currAirline, baseMat, currPath, uav.getSarImageLight());
|
String afterName = airlineExecId + "-" + base.getImageNo() + ".png";
|
||||||
|
String[] afterPath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, afterName);
|
||||||
|
|
||||||
|
JmImageRotateDTO rotateDTO = JmImageRotateDTO.rotate(currAirline.getTargetHeading(), currAirline.getDirection());
|
||||||
|
rotateDTO.setLightRate(uav.getSarImageLight());
|
||||||
|
base.setRotateAngle(rotateDTO.getAngle());
|
||||||
|
rotateDTO.setAngle(0); // 不旋转
|
||||||
|
generateAfterMat(baseMat, afterPath[0], rotateDTO);
|
||||||
|
saveImage(uav, airlineExecId, base, afterPath, imageFrame, frameNo, currAirline);
|
||||||
// 6.更新基准图坐标和帧号
|
// 6.更新基准图坐标和帧号
|
||||||
JmImage imageInfo = saveImage(uav, airlineExecId, base, imagePath, imageFrame, frameNo, lostImage);
|
//updateCoord(airlineExecId, currAirline, imageFrame);
|
||||||
|
|
||||||
long end = System.currentTimeMillis();
|
long end = System.currentTimeMillis();
|
||||||
log.info("生成" + imageFrame.getImageBitDeep()+"位雷达回传图像:帧序号" + frameNo + "," +
|
log.info("生成" + imageFrame.getImageBitDeep() + "位雷达回传图像:帧序号" + frameNo + "," +
|
||||||
imageInfo.getRelativePath() + ",耗时" + (end - start)/1000 + "秒");
|
base.getRelativePath() + ",耗时" + (end - start) / 1000 + "秒");
|
||||||
return imageInfo;
|
// 向前端推送
|
||||||
}
|
base.setJobId(base.getJobConfId());
|
||||||
|
sarBackWsService.sendImg(base);
|
||||||
/**
|
return base;
|
||||||
* 加载回传图像,先转置再转为Mat
|
|
||||||
*
|
|
||||||
* @param frameData 图像帧数据,包含参数信息和未转置的8位灰度图像数据
|
|
||||||
* @param imageFrame 图像帧对象
|
|
||||||
* @return 转置后的4通道图像Mat
|
|
||||||
*/
|
|
||||||
private Mat loadCurrImageMat(byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
|
||||||
Mat currImage = null;
|
|
||||||
int offset = imageFrame.IMAGE_OFFSET;
|
|
||||||
|
|
||||||
switch (imageFrame.getImgType()) {
|
|
||||||
case 0: // tif
|
|
||||||
currImage = ImageUtil.createImage(frameData, offset, imageFrame.getWidth(),
|
|
||||||
imageFrame.getHeight(), imageFrame.getImageBitDeep());
|
|
||||||
break;
|
|
||||||
case 1: // jpg,先去掉参数信息,只保留图像数据
|
|
||||||
case 4: { // png
|
|
||||||
byte[] validData = new byte[frameData.length - offset];
|
|
||||||
System.arraycopy(frameData, offset, validData, 0, validData.length);
|
|
||||||
currImage = ImageUtil.createImageFromJpg(validData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.error("不支持的图片类型imgType=" + imageFrame.getImgType() + ",丢弃图片");
|
|
||||||
}
|
|
||||||
if (currImage != null && currImage.empty()) {
|
|
||||||
log.error("图片数据是空的,数据长度:" + frameData.length);
|
|
||||||
currImage.release();
|
|
||||||
}
|
|
||||||
return currImage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,13 +182,52 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
OpenCVUtil.multiply(currImage , currMax / baseMax);
|
OpenCVUtil.multiply(currImage , currMax / baseMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mat baseMat = ImageUtil.join(baseImage, baseNo, currImage, currNo); // 会释放 currImage baseImage 资源
|
Mat baseMat = ImageUtil.join(baseImage, baseNo, currImage, currNo, base.getId() == null); // 会释放 currImage baseImage 资源
|
||||||
if (baseMat != null) { // 若拼接成功则回写硬盘,拼接失败按丢图处理,不改动基准图
|
if (baseMat != null) { // 若拼接成功则回写硬盘,拼接失败按丢图处理,不改动基准图
|
||||||
OpenCVUtil.write(imagePath, baseMat, false);
|
OpenCVUtil.write(imagePath, baseMat, false);
|
||||||
}
|
}
|
||||||
return baseMat;
|
return baseMat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Mat generateBaseMat2(JmImage base, Mat currImage, int currNo, float currMax, String imagePath, Integer baseNo) {
|
||||||
|
Mat baseImage = OpenCVUtil.read(imagePath);
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 分位数匹配逻辑
|
||||||
|
// ==============================
|
||||||
|
if (base != null && base.getId() != null
|
||||||
|
&& baseImage != null
|
||||||
|
&& currImage != null) {
|
||||||
|
|
||||||
|
double baseP = SarImageToneAdjuster.computePercentile(baseImage, 0.99);
|
||||||
|
double currP = SarImageToneAdjuster.computePercentile(currImage, 0.99);
|
||||||
|
|
||||||
|
if (baseP > 0 && currP > 0) {
|
||||||
|
|
||||||
|
double scale = baseP / currP;
|
||||||
|
|
||||||
|
// 限制比例,防止跳变
|
||||||
|
double MIN_SCALE = 0.7;
|
||||||
|
double MAX_SCALE = 1.5;
|
||||||
|
|
||||||
|
scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
|
||||||
|
|
||||||
|
OpenCVUtil.multiply(currImage, scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 原拼接逻辑不变
|
||||||
|
// ==============================
|
||||||
|
Mat baseMat = ImageUtil.join(baseImage, baseNo, currImage, currNo, base.getId() == null);
|
||||||
|
|
||||||
|
if (baseMat != null) {
|
||||||
|
OpenCVUtil.write(imagePath, baseMat, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseMat;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成后处理图,将拼接好的基准图转置并调整对比度
|
* 生成后处理图,将拼接好的基准图转置并调整对比度
|
||||||
*
|
*
|
||||||
@ -299,12 +236,45 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
* @param imagePath 后处理图路径,每条航线对应一张图,每次生成会覆盖
|
* @param imagePath 后处理图路径,每条航线对应一张图,每次生成会覆盖
|
||||||
* @param imageLight 图像亮度倍数,0是不调整
|
* @param imageLight 图像亮度倍数,0是不调整
|
||||||
*/
|
*/
|
||||||
private void generateAfterMat(JmAirlineStatusDTO currAirline, Mat baseMat, String imagePath, int imageLight) {
|
private void generateAfterMat(Mat baseMat, String imagePath, JmImageRotateDTO rotateDTO) {
|
||||||
// 后处理参数
|
// 后处理参数
|
||||||
JmImageRotateDTO rotateDTO = JmImageRotateDTO.rotate(currAirline.getTargetHeading(), currAirline.getDirection());
|
|
||||||
rotateDTO.setLightRate(imageLight);
|
|
||||||
Mat afterMat = ImageUtil.afterCreate(baseMat, rotateDTO); // 会释放 baseMat 资源
|
Mat afterMat = ImageUtil.afterCreate(baseMat, rotateDTO); // 会释放 baseMat 资源
|
||||||
sarWsAsyncService.write(imagePath, afterMat, true);
|
OpenCVUtil.write(imagePath, afterMat, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载图像坐标
|
||||||
|
* @param currAirline
|
||||||
|
* @param image
|
||||||
|
* @param imageFrame
|
||||||
|
* @param isFirst
|
||||||
|
*/
|
||||||
|
private void loadCoord(JmJobModeEnum jobMode, JmAirlineStatusDTO currAirline, JmImage image, SarBackImageFrameDTO imageFrame, boolean isFirst) {
|
||||||
|
double[] values = currAirline.getValues();
|
||||||
|
if (isFirst) {
|
||||||
|
if (values == null) {
|
||||||
|
values = new double[9];
|
||||||
|
currAirline.setValues(values);
|
||||||
|
}
|
||||||
|
if (values[4] != image.getImageNo() && image.getImageNo() > 1 && jobMode != JmJobModeEnum.CRUISE) { // 1、4用前一张图5、8坐标
|
||||||
|
// System.out.println("mode:" + jobMode.getText());
|
||||||
|
values[0] = values[5];
|
||||||
|
values[1] = values[6];
|
||||||
|
values[2] = values[7];
|
||||||
|
values[3] = values[8];
|
||||||
|
} else {
|
||||||
|
values[0] = imageFrame.getLon1();
|
||||||
|
values[1] = imageFrame.getLat1();
|
||||||
|
values[2] = imageFrame.getLon4();
|
||||||
|
values[3] = imageFrame.getLat4();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values[4] = image.getImageNo();
|
||||||
|
values[5] = imageFrame.getLon5();
|
||||||
|
values[6] = imageFrame.getLat5();
|
||||||
|
values[7] = imageFrame.getLon8();
|
||||||
|
values[8] = imageFrame.getLat8();
|
||||||
|
image.updateCoord(values[0], values[1], values[2], values[3], values[5], values[6], values[7], values[8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -313,24 +283,16 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
* @param airlineId 航线执行ID
|
* @param airlineId 航线执行ID
|
||||||
*/
|
*/
|
||||||
private JmImage saveImage(JmUavStatusDTO uav, Long airlineId, JmImage image, String[] imagePath,
|
private JmImage saveImage(JmUavStatusDTO uav, Long airlineId, JmImage image, String[] imagePath,
|
||||||
SarBackImageFrameDTO imageFrame, int frameNo, boolean lostImage) {
|
SarBackImageFrameDTO imageFrame, int frameNo, JmAirlineStatusDTO currAirline) {
|
||||||
boolean isFirst = image.getId() == null;
|
boolean isFirst = image.getId() == null;
|
||||||
|
// loadCoord(uav.getJobMode(), currAirline, image, imageFrame, isFirst);
|
||||||
// 拼接后的图片的四角坐标
|
loadCoord(JmJobModeEnum.CREATE, currAirline, image, imageFrame, isFirst);
|
||||||
double minLon = isFirst ? MathUtil.min(imageFrame.getLon1(), imageFrame.getLon4(), imageFrame.getLon5(), imageFrame.getLon8())
|
|
||||||
: MathUtil.min(imageFrame.getLon1(), imageFrame.getLon4(), imageFrame.getLon5(), imageFrame.getLon8(), image.getLeft1Lon(), image.getRight1Lon());
|
|
||||||
double maxLon = isFirst ? MathUtil.max(imageFrame.getLon1(), imageFrame.getLon4(), imageFrame.getLon5(), imageFrame.getLon8())
|
|
||||||
: MathUtil.max(imageFrame.getLon1(), imageFrame.getLon4(), imageFrame.getLon5(), imageFrame.getLon8(), image.getLeft1Lon(), image.getRight1Lon());
|
|
||||||
double minLat = isFirst ? MathUtil.min(imageFrame.getLat1(), imageFrame.getLat4(), imageFrame.getLat5(), imageFrame.getLat8())
|
|
||||||
: MathUtil.min(imageFrame.getLat1(), imageFrame.getLat4(), imageFrame.getLat5(), imageFrame.getLat8(), image.getLeft1Lat(), image.getLeft2Lat());
|
|
||||||
double maxLat = isFirst ? MathUtil.max(imageFrame.getLat1(), imageFrame.getLat4(), imageFrame.getLat5(), imageFrame.getLat8())
|
|
||||||
: MathUtil.max(imageFrame.getLat1(), imageFrame.getLat4(), imageFrame.getLat5(), imageFrame.getLat8(), image.getLeft1Lat(), image.getLeft2Lat());
|
|
||||||
// 更新坐标和序号
|
|
||||||
image.updateCoord(minLon, maxLon, minLat, maxLat);
|
|
||||||
|
|
||||||
Float currMax = !isFirst && image.getMax() > imageFrame.getMax() ? image.getMax() : imageFrame.getMax();
|
Float currMax = !isFirst && image.getMax() > imageFrame.getMax() ? image.getMax() : imageFrame.getMax();
|
||||||
image.setMax(currMax);
|
image.setMax(currMax);
|
||||||
image.setImageTime(imageFrame.getDate());
|
image.setImageTime(imageFrame.getDate());
|
||||||
|
image.setFrameTo(frameNo);
|
||||||
|
image.setBrightness(uav.getSarImageLight());
|
||||||
|
|
||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
File file = new File(imagePath[0]);
|
File file = new File(imagePath[0]);
|
||||||
@ -348,11 +310,11 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
imageService.updateNotNull(image);
|
imageService.updateNotNull(image);
|
||||||
}
|
}
|
||||||
// 前端需要
|
// 前端需要
|
||||||
image.setJobId(uav.getJobId()); // 前端通过任务配置ID关联任务
|
image.setJobConfId(uav.getJobId());
|
||||||
image.setJobName(uav.getJobName());
|
image.setJobName(uav.getJobName());
|
||||||
image.setUavName(uav.getUavName());
|
image.setUavName(uav.getUavName());
|
||||||
image.setPayloadName(uav.getSarName());
|
image.setPayloadName(uav.getSarName());
|
||||||
image.setBrightness(uav.getSarImageLight());
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,18 +2,24 @@ package com.zhangy.skyeye.sar.util;
|
|||||||
|
|
||||||
import org.opencv.core.*;
|
import org.opencv.core.*;
|
||||||
import org.opencv.imgcodecs.Imgcodecs;
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.CLAHE;
|
||||||
import org.opencv.imgproc.Imgproc;
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
import org.opencv.core.Core;
|
import org.opencv.core.Core;
|
||||||
import org.opencv.core.CvType;
|
import org.opencv.core.CvType;
|
||||||
import org.opencv.core.Mat;
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.photo.Photo;
|
||||||
|
import org.opencv.photo.Tonemap;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SAR图像亮度修正,在拼接图片后食用
|
* SAR图像亮度修正,在拼接图片后食用
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class SarImageToneAdjuster {
|
public class SarImageToneAdjuster {
|
||||||
|
|
||||||
// 示例
|
// 示例
|
||||||
@ -643,4 +649,177 @@ public class SarImageToneAdjuster {
|
|||||||
|
|
||||||
image.put(0, 0, data);
|
image.put(0, 0, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reinhard 局部色调映射
|
||||||
|
*/
|
||||||
|
public static Mat reinhardToneMapping(Mat src,
|
||||||
|
float gamma,
|
||||||
|
float intensity,
|
||||||
|
float lightAdapt,
|
||||||
|
float colorAdapt) {
|
||||||
|
|
||||||
|
Mat srcFloat = new Mat();
|
||||||
|
src.convertTo(srcFloat, CvType.CV_32FC3, 1.0 / 255.0);
|
||||||
|
|
||||||
|
// 注意这里的类型
|
||||||
|
Tonemap tonemap = Photo.createTonemapReinhard(
|
||||||
|
gamma,
|
||||||
|
intensity,
|
||||||
|
lightAdapt,
|
||||||
|
colorAdapt
|
||||||
|
);
|
||||||
|
|
||||||
|
Mat dstFloat = new Mat();
|
||||||
|
tonemap.process(srcFloat, dstFloat);
|
||||||
|
|
||||||
|
Mat dst = new Mat();
|
||||||
|
dstFloat.convertTo(dst, CvType.CV_8UC3, 255.0);
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对输入图像做 CLAHE (在 LAB 空间)
|
||||||
|
*/
|
||||||
|
public static Mat applyCLAHEGray(Mat src,
|
||||||
|
double clipLimit,
|
||||||
|
Size tileGridSize) {
|
||||||
|
|
||||||
|
CLAHE clahe = Imgproc.createCLAHE();
|
||||||
|
clahe.setClipLimit(clipLimit);
|
||||||
|
clahe.setTilesGridSize(tileGridSize);
|
||||||
|
|
||||||
|
Mat dst = new Mat();
|
||||||
|
clahe.apply(src, dst);
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mat enhanceSarAuto(Mat src) {
|
||||||
|
|
||||||
|
Mat gray = new Mat();
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// 转单通道
|
||||||
|
// =========================
|
||||||
|
if (src.channels() == 1) {
|
||||||
|
gray = src;
|
||||||
|
}
|
||||||
|
else if (src.channels() == 3) {
|
||||||
|
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
}
|
||||||
|
else if (src.channels() == 4) {
|
||||||
|
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGRA2GRAY);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Unsupported channel number");
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// 调用增强
|
||||||
|
// =========================
|
||||||
|
return enhanceSarImage(gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mat enhanceSarImage(Mat src) {
|
||||||
|
|
||||||
|
if (src.channels() != 1) {
|
||||||
|
log.info("====channels: " + src.channels() + "======");
|
||||||
|
throw new IllegalArgumentException("Only single channel supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat floatMat = new Mat();
|
||||||
|
|
||||||
|
// 1️⃣ 转 float
|
||||||
|
if (src.type() == CvType.CV_16U)
|
||||||
|
src.convertTo(floatMat, CvType.CV_32F, 1.0 / 65535.0);
|
||||||
|
else if (src.type() == CvType.CV_8U)
|
||||||
|
src.convertTo(floatMat, CvType.CV_32F, 1.0 / 255.0);
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Unsupported type");
|
||||||
|
|
||||||
|
// 2️⃣ log
|
||||||
|
Core.add(floatMat, Scalar.all(1e-6), floatMat);
|
||||||
|
Core.log(floatMat, floatMat);
|
||||||
|
|
||||||
|
// 3️⃣ 百分位裁剪(稳定)
|
||||||
|
Mat flat = floatMat.reshape(1,1);
|
||||||
|
Mat sorted = flat.clone();
|
||||||
|
Core.sort(sorted, sorted, Core.SORT_ASCENDING);
|
||||||
|
|
||||||
|
int total = sorted.cols();
|
||||||
|
double low = sorted.get(0, (int)(total * 0.01))[0];
|
||||||
|
double high = sorted.get(0, (int)(total * 0.995))[0];
|
||||||
|
|
||||||
|
Core.min(floatMat, new Scalar(high), floatMat);
|
||||||
|
Core.max(floatMat, new Scalar(low), floatMat);
|
||||||
|
|
||||||
|
Core.subtract(floatMat, new Scalar(low), floatMat);
|
||||||
|
Core.divide(floatMat, new Scalar(high - low), floatMat);
|
||||||
|
|
||||||
|
// 4️⃣ 转 8bit
|
||||||
|
Mat img8 = new Mat();
|
||||||
|
floatMat.convertTo(img8, CvType.CV_8U, 255.0);
|
||||||
|
|
||||||
|
// 5️⃣ 去 speckle
|
||||||
|
Imgproc.medianBlur(img8, img8, 3);
|
||||||
|
|
||||||
|
// 6️⃣ 动态 tile 计算(关键)
|
||||||
|
int targetTileSize = 512; // 每块目标大小
|
||||||
|
int tilesX = Math.max(1, src.cols() / targetTileSize);
|
||||||
|
int tilesY = Math.max(1, src.rows() / targetTileSize);
|
||||||
|
|
||||||
|
CLAHE clahe = Imgproc.createCLAHE();
|
||||||
|
|
||||||
|
// 自适应 clip
|
||||||
|
MatOfDouble mean = new MatOfDouble();
|
||||||
|
MatOfDouble std = new MatOfDouble();
|
||||||
|
Core.meanStdDev(img8, mean, std);
|
||||||
|
|
||||||
|
double s = std.get(0,0)[0];
|
||||||
|
double clip;
|
||||||
|
|
||||||
|
if (s < 20)
|
||||||
|
clip = 3.0;
|
||||||
|
else if (s < 40)
|
||||||
|
clip = 2.5;
|
||||||
|
else
|
||||||
|
clip = 2.0;
|
||||||
|
|
||||||
|
clahe.setClipLimit(clip);
|
||||||
|
clahe.setTilesGridSize(new Size(tilesX, tilesY));
|
||||||
|
|
||||||
|
Mat claheOut = new Mat();
|
||||||
|
clahe.apply(img8, claheOut);
|
||||||
|
|
||||||
|
// 7️⃣ 轻锐化(减弱避免放大缝)
|
||||||
|
Mat blur = new Mat();
|
||||||
|
Imgproc.GaussianBlur(claheOut, blur, new Size(0,0), 1.0);
|
||||||
|
|
||||||
|
Mat sharpened = new Mat();
|
||||||
|
Core.addWeighted(claheOut, 1.1, blur, -0.1, 0, sharpened);
|
||||||
|
|
||||||
|
return sharpened;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public double computePercentile(Mat mat, double percentile) {
|
||||||
|
|
||||||
|
if (mat.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Mat flat = mat.reshape(1, 1); // 拉平成1行
|
||||||
|
Mat sorted = new Mat();
|
||||||
|
|
||||||
|
Core.sort(flat, sorted, Core.SORT_ASCENDING);
|
||||||
|
|
||||||
|
int total = sorted.cols();
|
||||||
|
int index = (int)(percentile * total);
|
||||||
|
|
||||||
|
index = Math.min(Math.max(index, 0), total - 1);
|
||||||
|
|
||||||
|
double[] value = sorted.get(0, index);
|
||||||
|
|
||||||
|
return value != null ? value[0] : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ mybatis-plus:
|
|||||||
table-underline: true
|
table-underline: true
|
||||||
configuration:
|
configuration:
|
||||||
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
|
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
|
||||||
#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
call-setters-on-nulls: true
|
call-setters-on-nulls: true
|
||||||
# Jasypt 配置
|
# Jasypt 配置
|
||||||
jasypt:
|
jasypt:
|
||||||
@ -76,7 +76,9 @@ skyeye:
|
|||||||
sar:
|
sar:
|
||||||
image:
|
image:
|
||||||
type: 1
|
type: 1
|
||||||
max: 409600000
|
default-level: 15
|
||||||
|
max-level: 18
|
||||||
|
count: 5
|
||||||
udp:
|
udp:
|
||||||
status:
|
status:
|
||||||
answer-timeout: 2
|
answer-timeout: 2
|
||||||
|
|||||||
@ -98,13 +98,15 @@ CREATE TABLE `jm_image` (
|
|||||||
`right1_lat` decimal(11,8) DEFAULT NULL COMMENT '右上纬度',
|
`right1_lat` decimal(11,8) DEFAULT NULL COMMENT '右上纬度',
|
||||||
`right2_lon` decimal(11,8) DEFAULT NULL COMMENT '右下经度',
|
`right2_lon` decimal(11,8) DEFAULT NULL COMMENT '右下经度',
|
||||||
`right2_lat` decimal(11,8) DEFAULT NULL COMMENT '右下纬度',
|
`right2_lat` decimal(11,8) DEFAULT NULL COMMENT '右下纬度',
|
||||||
`frame_no` int DEFAULT NULL COMMENT '图像帧序号',
|
|
||||||
`max` decimal(20,6) DEFAULT NULL COMMENT '归一化前最大值',
|
`max` decimal(20,6) DEFAULT NULL COMMENT '归一化前最大值',
|
||||||
`image_time` datetime DEFAULT NULL COMMENT '图像生成时间',
|
`image_time` datetime DEFAULT NULL COMMENT '图像生成时间',
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '文件上传时间',
|
`create_time` datetime DEFAULT NULL COMMENT '文件上传时间',
|
||||||
|
`tail_level` int DEFAULT NULL COMMENT '层级',
|
||||||
|
`rotate_angle` decimal(11,8) DEFAULT NULL COMMENT '旋转角度',
|
||||||
`image_no` int DEFAULT '1' COMMENT '图像编号',
|
`image_no` int DEFAULT '1' COMMENT '图像编号',
|
||||||
`start_frame_no` int DEFAULT NULL,
|
`frame_from` int DEFAULT NULL COMMENT '起始帧',
|
||||||
`end_frame_no` int DEFAULT NULL,
|
`frame_to` int DEFAULT NULL COMMENT '结束帧',
|
||||||
|
`brightness` int DEFAULT NULL COMMENT '亮度',
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=10542 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='sar图像';
|
) ENGINE=InnoDB AUTO_INCREMENT=10542 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='sar图像';
|
||||||
|
|
||||||
|
|||||||
@ -3,15 +3,17 @@
|
|||||||
|
|
||||||
<mapper namespace="com.zhangy.skyeye.jm.mapper.JmImageMapper">
|
<mapper namespace="com.zhangy.skyeye.jm.mapper.JmImageMapper">
|
||||||
<sql id="selectSql">
|
<sql id="selectSql">
|
||||||
select
|
select m.id,
|
||||||
m.id,
|
|
||||||
m.name,
|
m.name,
|
||||||
m.job_id, j.name as job_name,
|
m.image_no,
|
||||||
|
m.frame_from,
|
||||||
|
m.frame_to,
|
||||||
|
m.job_id,
|
||||||
|
j.name as job_name,
|
||||||
m.uav_id,
|
m.uav_id,
|
||||||
m.payload_id,
|
m.payload_id,
|
||||||
m.airline_id,
|
m.airline_id,
|
||||||
m.file_id,
|
m.file_id,
|
||||||
m.image_no,
|
|
||||||
m.max,
|
m.max,
|
||||||
m.left1_lon,
|
m.left1_lon,
|
||||||
m.left1_lat,
|
m.left1_lat,
|
||||||
@ -23,13 +25,19 @@
|
|||||||
m.right2_lat,
|
m.right2_lat,
|
||||||
m.image_time,
|
m.image_time,
|
||||||
m.create_time,
|
m.create_time,
|
||||||
|
m.tail_level,
|
||||||
|
m.rotate_angle,
|
||||||
|
m.brightness,
|
||||||
f.name as file_name,
|
f.name as file_name,
|
||||||
f.type as file_type,
|
f.type as file_type,
|
||||||
f.path as file_path,
|
f.path as file_path,
|
||||||
f.relative_path as relative_path
|
f.relative_path as relative_path,
|
||||||
|
j.id as job_conf_id,
|
||||||
|
j.name as job_name
|
||||||
from jm_image m
|
from jm_image m
|
||||||
left join sys_file f on f.id = m.file_id
|
left join sys_file f on f.id = m.file_id
|
||||||
left join jm_job j on j.id = m.job_id
|
left join jm_job_exec je on je.id = m.job_id
|
||||||
|
left join jm_job j on j.id = je.conf_id
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectPage" resultType="com.zhangy.skyeye.jm.entity.JmImage">
|
<select id="selectPage" resultType="com.zhangy.skyeye.jm.entity.JmImage">
|
||||||
@ -47,11 +55,13 @@
|
|||||||
<if test="name != null and name != ''">
|
<if test="name != null and name != ''">
|
||||||
and f.name like concat('%', #{name}, '%')
|
and f.name like concat('%', #{name}, '%')
|
||||||
</if>
|
</if>
|
||||||
|
order by j.create_time desc, m.create_time
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectByJob" resultType="com.zhangy.skyeye.jm.entity.JmImage">
|
<select id="selectByJob" resultType="com.zhangy.skyeye.jm.entity.JmImage">
|
||||||
<include refid="selectSql"/>
|
<include refid="selectSql"/>
|
||||||
where m.job_id in
|
where
|
||||||
|
m.job_id in
|
||||||
<foreach item="item" collection="array" open="(" separator="," close=")">
|
<foreach item="item" collection="array" open="(" separator="," close=")">
|
||||||
#{item}
|
#{item}
|
||||||
</foreach>
|
</foreach>
|
||||||
@ -86,6 +96,19 @@
|
|||||||
order by m.image_no
|
order by m.image_no
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectLastByAirline" resultType="com.zhangy.skyeye.jm.entity.JmImage">
|
||||||
|
<include refid="selectSql"/>
|
||||||
|
where m.airline_id in
|
||||||
|
<foreach item="item" collection="array" open="(" separator="," close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
<if test="type != null">
|
||||||
|
and f.type = #{type}
|
||||||
|
</if>
|
||||||
|
order by m.image_no desc
|
||||||
|
limit 0,1
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectById" resultType="com.zhangy.skyeye.jm.entity.JmImage">
|
<select id="selectById" resultType="com.zhangy.skyeye.jm.entity.JmImage">
|
||||||
<include refid="selectSql"/>
|
<include refid="selectSql"/>
|
||||||
where m.id in
|
where m.id in
|
||||||
@ -117,7 +140,7 @@
|
|||||||
payload_id,
|
payload_id,
|
||||||
airline_id,
|
airline_id,
|
||||||
file_id,
|
file_id,
|
||||||
image_no,
|
rotate_angle,
|
||||||
max,
|
max,
|
||||||
left1_lon,
|
left1_lon,
|
||||||
left1_lat,
|
left1_lat,
|
||||||
@ -127,8 +150,13 @@
|
|||||||
right1_lat,
|
right1_lat,
|
||||||
right2_lon,
|
right2_lon,
|
||||||
right2_lat,
|
right2_lat,
|
||||||
|
tail_level,
|
||||||
image_time,
|
image_time,
|
||||||
create_time
|
create_time,
|
||||||
|
image_no,
|
||||||
|
frame_from,
|
||||||
|
frame_to,
|
||||||
|
brightness
|
||||||
) values
|
) values
|
||||||
<foreach item="item" index="index" collection="array" separator=",">
|
<foreach item="item" index="index" collection="array" separator=",">
|
||||||
(
|
(
|
||||||
@ -139,7 +167,7 @@
|
|||||||
#{item.payloadId},
|
#{item.payloadId},
|
||||||
#{item.airlineId},
|
#{item.airlineId},
|
||||||
#{item.fileId},
|
#{item.fileId},
|
||||||
#{item.imageNo},
|
#{item.rotateAngle},
|
||||||
#{item.max},
|
#{item.max},
|
||||||
#{item.left1Lon},
|
#{item.left1Lon},
|
||||||
#{item.left1Lat},
|
#{item.left1Lat},
|
||||||
@ -149,8 +177,13 @@
|
|||||||
#{item.right1Lat},
|
#{item.right1Lat},
|
||||||
#{item.right2Lon},
|
#{item.right2Lon},
|
||||||
#{item.right2Lat},
|
#{item.right2Lat},
|
||||||
|
#{item.tailLevel},
|
||||||
#{item.imageTime},
|
#{item.imageTime},
|
||||||
#{item.createTime}
|
#{item.createTime},
|
||||||
|
#{item.imageNo},
|
||||||
|
#{item.frameFrom},
|
||||||
|
#{item.frameTo},
|
||||||
|
#{item.brightness}
|
||||||
)
|
)
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
@ -165,6 +198,7 @@
|
|||||||
<if test="airlineId != null">airline_id = #{airlineId},</if>
|
<if test="airlineId != null">airline_id = #{airlineId},</if>
|
||||||
<if test="fileId != null">file_id = #{fileId},</if>
|
<if test="fileId != null">file_id = #{fileId},</if>
|
||||||
<if test="max != null">max = #{max},</if>
|
<if test="max != null">max = #{max},</if>
|
||||||
|
<if test="rotateAngle != null">rotate_angle = #{rotateAngle},</if>
|
||||||
<if test="left1Lon != null">left1_lon = #{left1Lon},</if>
|
<if test="left1Lon != null">left1_lon = #{left1Lon},</if>
|
||||||
<if test="left1Lat != null">left1_lat = #{left1Lat},</if>
|
<if test="left1Lat != null">left1_lat = #{left1Lat},</if>
|
||||||
<if test="left2Lon != null">left2_lon = #{left2Lon},</if>
|
<if test="left2Lon != null">left2_lon = #{left2Lon},</if>
|
||||||
@ -175,6 +209,11 @@
|
|||||||
<if test="right2Lat != null">right2_lat = #{right2Lat},</if>
|
<if test="right2Lat != null">right2_lat = #{right2Lat},</if>
|
||||||
<if test="imageTime != null">image_time = #{imageTime},</if>
|
<if test="imageTime != null">image_time = #{imageTime},</if>
|
||||||
<if test="createTime != null">create_time = #{createTime},</if>
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
|
<if test="tailLevel != null">tail_level = #{tailLevel},</if>
|
||||||
|
<if test="imageNo != null">image_no = #{imageNo},</if>
|
||||||
|
<if test="frameFrom != null">frame_from = #{frameFrom},</if>
|
||||||
|
<if test="frameTo != null">frame_to = #{frameTo},</if>
|
||||||
|
<if test="brightness != null">brightness = #{brightness},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
@ -189,6 +228,7 @@
|
|||||||
airline_id = #{airlineId},
|
airline_id = #{airlineId},
|
||||||
file_id = #{fileId},
|
file_id = #{fileId},
|
||||||
max = #{max},
|
max = #{max},
|
||||||
|
rotate_angle = #{rotateAngle},
|
||||||
left1_lon = #{left1Lon},
|
left1_lon = #{left1Lon},
|
||||||
left1_lat = #{left1Lat},
|
left1_lat = #{left1Lat},
|
||||||
left2_lon = #{left2Lon},
|
left2_lon = #{left2Lon},
|
||||||
@ -199,6 +239,8 @@
|
|||||||
right2_lat = #{right2Lat},
|
right2_lat = #{right2Lat},
|
||||||
image_time = #{imageTime},
|
image_time = #{imageTime},
|
||||||
create_time = #{createTime},
|
create_time = #{createTime},
|
||||||
|
tail_level = #{tailLevel},
|
||||||
|
brightness = #{brightness},
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
window.config = {
|
window.config = {
|
||||||
env: 'offline', //online
|
env: 'offline', //online
|
||||||
//api: 'http://127.0.0.1:9116/', // 外网服务器,
|
//api: 'http://127.0.0.1:9116/', // 外网服务器,
|
||||||
api: 'http://182.92.203.107:9116',
|
api: 'http://127.0.0.1:9116',
|
||||||
socket: 'http://182.92.203.107:9116', //外网服务器,
|
socket: 'http://127.0.0.1:9116', //外网服务器,
|
||||||
imagePath: 'http://182.92.203.107:8080/files',
|
imagePath: 'http://127.0.0.1:8080/files',
|
||||||
arithmeticPath: 'http://127.0.0.1:18090/ktkx/UavPlanning/SAR',
|
arithmeticPath: 'http://127.0.0.1:18090/ktkx/UavPlanning/SAR',
|
||||||
tokenKey: 'accessToken',
|
tokenKey: 'accessToken',
|
||||||
refreshTokenKey: 'refreshToken',
|
refreshTokenKey: 'refreshToken',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user