Merge branch 'refs/heads/main' into dev_20260130_RemoveRedis

This commit is contained in:
longguancheng 2026-02-10 10:30:08 +08:00
commit a3b29f6b90
13 changed files with 317 additions and 16 deletions

View File

@ -3,6 +3,7 @@ package com.zhangy.skyeye.jm.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zhangy.skyeye.jm.dto.JmJobDTO; import com.zhangy.skyeye.jm.dto.JmJobDTO;
import com.zhangy.skyeye.jm.dto.JmJobPageDTO; import com.zhangy.skyeye.jm.dto.JmJobPageDTO;
import com.zhangy.skyeye.jm.entity.JmImage;
import com.zhangy.skyeye.jm.service.JmJobExecService; import com.zhangy.skyeye.jm.service.JmJobExecService;
import com.zhangy.skyeye.jm.service.JmJobService; import com.zhangy.skyeye.jm.service.JmJobService;
import com.zhangy.skyeye.jm.service.JmJobStatusService; import com.zhangy.skyeye.jm.service.JmJobStatusService;
@ -55,4 +56,10 @@ public class JmJobExecController {
}); });
return page; return page;
} }
@RequestMapping("/brightness")
public Object adjustBrightness(@Valid @RequestBody JmImage param) {
jobExecService.adjustBrightness(param);
return "成功调整亮度";
}
} }

View File

@ -82,6 +82,9 @@ public class JmImage extends GeoTiffDTO {
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date imageTime; private Date imageTime;
/** 图像亮度 */
private Integer brightness;
/** 图像识别结果 */ /** 图像识别结果 */
private List<JmImageItem> itemList; private List<JmImageItem> itemList;

View File

@ -2,6 +2,7 @@ package com.zhangy.skyeye.jm.service;
import com.zhangy.skyeye.jm.dto.JmJobDTO; import com.zhangy.skyeye.jm.dto.JmJobDTO;
import com.zhangy.skyeye.jm.dto.JmJobQueryDTO; import com.zhangy.skyeye.jm.dto.JmJobQueryDTO;
import com.zhangy.skyeye.jm.entity.JmImage;
import com.zhangy.skyeye.jm.entity.JmJobExec; import com.zhangy.skyeye.jm.entity.JmJobExec;
import java.util.List; import java.util.List;
@ -54,4 +55,6 @@ public interface JmJobExecService {
* 按任务配置删除任务执行航线执行 * 按任务配置删除任务执行航线执行
*/ */
void deleteByJobConf(Long... jobConfId); void deleteByJobConf(Long... jobConfId);
void adjustBrightness(JmImage param);
} }

View File

@ -1,20 +1,16 @@
package com.zhangy.skyeye.jm.service.impl; package com.zhangy.skyeye.jm.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.exception.ServiceException;
import com.zhangy.skyeye.common.extend.util.ObjectUtil; import com.zhangy.skyeye.common.extend.util.ObjectUtil;
import com.zhangy.skyeye.jm.dto.JmJobDTO; import com.zhangy.skyeye.jm.dto.JmJobDTO;
import com.zhangy.skyeye.jm.dto.JmJobQueryDTO; import com.zhangy.skyeye.jm.dto.JmJobQueryDTO;
import com.zhangy.skyeye.jm.entity.JmAirlineExec; import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
import com.zhangy.skyeye.jm.entity.JmJobExec; import com.zhangy.skyeye.jm.entity.*;
import com.zhangy.skyeye.jm.entity.JmJobPoint;
import com.zhangy.skyeye.jm.entity.JmJobUav;
import com.zhangy.skyeye.jm.event.JmJobStatusInitEvent; import com.zhangy.skyeye.jm.event.JmJobStatusInitEvent;
import com.zhangy.skyeye.jm.mapper.JmJobExecMapper; import com.zhangy.skyeye.jm.mapper.JmJobExecMapper;
import com.zhangy.skyeye.jm.service.JmAirlineExecService; import com.zhangy.skyeye.jm.service.*;
import com.zhangy.skyeye.jm.service.JmJobExecService;
import com.zhangy.skyeye.jm.service.JmJobPointService;
import com.zhangy.skyeye.jm.service.JmJobUavService;
import com.zhangy.skyeye.publics.consts.ExecStatusEnum; import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -42,6 +38,12 @@ public class JmJobExecServiceImpl implements JmJobExecService {
@Autowired @Autowired
private ApplicationEventPublisher eventPublisher; private ApplicationEventPublisher eventPublisher;
@Autowired
private JmJobStatusService jobStatusService;
@Autowired
private JmJobService jobService;
@Override @Override
public void initExecutingJobs() { public void initExecutingJobs() {
List<JmJobDTO> executingJobs = this.selectWorking(); List<JmJobDTO> executingJobs = this.selectWorking();
@ -176,4 +178,26 @@ public class JmJobExecServiceImpl implements JmJobExecService {
jmAirlineExecService.deleteByJob(jobConfId); jmAirlineExecService.deleteByJob(jobConfId);
jobExecMapper.deleteByConf(jobConfId); jobExecMapper.deleteByConf(jobConfId);
} }
@Override
public void adjustBrightness(JmImage param) {
// update cache in order that the next coming image uses the new brightness
JmUavStatusDTO execSar = jobStatusService.getUav(param.getUavId());
execSar.setSarImageLight(param.getBrightness());
// update db in order that the next run of this job use the new brightness
JmJobDTO job = jobService.selectInfo(param.getJobId());
JmJobUav theUav = job.getUavList().stream()
.filter(uav -> uav.getUavId().equals(param.getUavId()))
.findFirst().orElse(null);
if (theUav != null) {
JmJobPayload theSar = theUav.getPayloadList().stream()
.filter(sar -> sar.getPayloadId().equals(param.getPayloadId()))
.findFirst().orElse(null);
if (theSar != null) {
theSar.setImageLight(param.getBrightness());
jobService.updateNotNull(job);
}
}
}
} }

View File

@ -324,7 +324,7 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
distance += uav.getDistance(); distance += uav.getDistance();
distanced += uav.getDistanced(); distanced += uav.getDistanced();
} }
return distanced * 100 / distance; return (distance == 0) ? 1 : (distanced * 100 / distance);
} }
/** /**

View File

@ -16,6 +16,7 @@ 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.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.SarImageToneAdjuster; import com.zhangy.skyeye.sar.util.SarImageToneAdjuster;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -190,6 +191,9 @@ public class SarImageServiceImpl implements ISarImageService {
// 拆分多张图片去掉自适应调整 // 拆分多张图片去掉自适应调整
if (IMG_MAX_WITH > 20000) { if (IMG_MAX_WITH > 20000) {
SarImageToneAdjuster.autoToneWithClipping(baseMat, 0.5f); SarImageToneAdjuster.autoToneWithClipping(baseMat, 0.5f);
// RadarDisplayOptions options = new RadarDisplayOptions();
// SarImageToneAdjuster.processForDisplayMat(baseMat, options);
// SarImageToneAdjuster.applyPseudoColorMat(baseMat, options);
} }
// 5.保存后处理图异步用于前端显示 // 5.保存后处理图异步用于前端显示
@ -326,6 +330,7 @@ public class SarImageServiceImpl implements ISarImageService {
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;
} }
} }

View File

@ -0,0 +1,20 @@
package com.zhangy.skyeye.sar.util;
public class RadarDisplayOptions {
/** 是否启用 log 压缩 */
public boolean enableLog = false;
/** gamma 值enableLog=false 时生效) */
public double gamma16 = 0.75;
public double gamma8 = 0.6;
/** 是否启用伪彩 */
public boolean enablePseudoColor = true;
/** 使用的 LUT */
public RadarPseudoColorLUT lut = RadarPseudoColorLUT.IRON;
/** clip 百分比,直接复用你原来的逻辑 */
// public float clipPercentage = 0.1f;
}

View File

@ -0,0 +1,22 @@
package com.zhangy.skyeye.sar.util;
public enum RadarPseudoColorLUT {
IRON(new int[]{
0x000000, 0x200030, 0x400060, 0x600090,
0x8000C0, 0xA000E0, 0xC040FF, 0xE080FF,
0xFFB000, 0xFFD000, 0xFFFF80
}),
JET(new int[]{
0x00007F, 0x0000FF, 0x007FFF,
0x00FF7F, 0x7FFF00, 0xFF7F00,
0xFF0000
});
public final int[] colors;
RadarPseudoColorLUT(int[] colors) {
this.colors = colors;
}
}

View File

@ -48,10 +48,12 @@ public class SarImageToneAdjuster {
// 处理8位图像 // 处理8位图像
if (depth == CvType.CV_8U) { if (depth == CvType.CV_8U) {
// preStretch8Bit(image, channels);
process8BitImage(image, clipPercentage, channels); process8BitImage(image, clipPercentage, channels);
} }
// 处理16位图像 // 处理16位图像
else if (depth == CvType.CV_16U) { else if (depth == CvType.CV_16U) {
// preStretch16Bit(image, channels);
process16BitImage(image, clipPercentage, channels); process16BitImage(image, clipPercentage, channels);
} }
else { else {
@ -441,4 +443,204 @@ public class SarImageToneAdjuster {
lut.put(0, 0, lutData); lut.put(0, 0, lutData);
return lut; return lut;
} }
public static void processForDisplayMat(Mat mat, RadarDisplayOptions options) {
int depth = mat.depth();
// 动态范围压缩SAR 显示核心
if (options.enableLog) {
applyLogCompressionMat(mat, depth == CvType.CV_16U);
if (depth == CvType.CV_16U) {
mat.convertTo(mat, CvType.CV_8U, 255.0 / 65535.0);
}
} else {
if (depth == CvType.CV_16U) {
applyGamma16U(mat, options.gamma16);
mat.convertTo(mat, CvType.CV_8U, 255.0 / 65535.0);
} else {
applyGamma8U(mat, options.gamma8);
}
}
}
private static void applyLogCompressionMat(Mat mat, boolean is16Bit) {
int rows = mat.rows();
int cols = mat.cols();
double maxValue = is16Bit ? 65535.0 : 255.0;
double logMax = Math.log(maxValue + 1.0);
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
double v = mat.get(y, x)[0];
double lv = Math.log(v + 1.0) / logMax;
mat.put(y, x, lv * maxValue);
}
}
}
private static void applyGamma8U(Mat image, double gamma) {
int channels = image.channels();
int total = (int) image.total() * channels;
byte[] data = new byte[total];
image.get(0, 0, data);
// 预计算 LUT非常重要速度快
byte[] lut = new byte[256];
for (int i = 0; i < 256; i++) {
double normalized = i / 255.0;
double corrected = Math.pow(normalized, gamma);
lut[i] = (byte) Math.min(255, Math.max(0, (int) (corrected * 255.0 + 0.5)));
}
for (int i = 0; i < total; i++) {
int v = data[i] & 0xFF;
data[i] = lut[v];
}
image.put(0, 0, data);
}
private static void applyGamma16U(Mat image, double gamma) {
int channels = image.channels();
int total = (int) image.total() * channels;
short[] data = new short[total];
image.get(0, 0, data);
for (int i = 0; i < total; i++) {
int v = data[i] & 0xFFFF;
double normalized = v / 65535.0;
double corrected = Math.pow(normalized, gamma);
int out = (int) (corrected * 65535.0 + 0.5);
data[i] = (short) Math.min(65535, Math.max(0, out));
}
image.put(0, 0, data);
}
public static void applyPseudoColorMat(Mat gray, RadarDisplayOptions options) {
if (!options.enablePseudoColor) {
return;
}
Mat color = new Mat();
Imgproc.cvtColor(gray, color, Imgproc.COLOR_GRAY2BGR);
for (int y = 0; y < gray.rows(); y++) {
for (int x = 0; x < gray.cols(); x++) {
int v = (int) gray.get(y, x)[0];
if (gray.depth() == CvType.CV_16U) {
v = v >> 8; // 16bit 8bit 映射工业常用
}
int rgb = mapToLUT(v, options.lut.colors);
color.put(y, x,
(rgb >> 16) & 0xFF,
(rgb >> 8) & 0xFF,
rgb & 0xFF
);
}
}
// 用伪彩图替换原 Mat显示专用
gray.release();
color.copyTo(gray);
}
private static int mapToLUT(int v, int[] keyColors) {
if (v <= 0) return keyColors[0];
if (v >= 255) return keyColors[keyColors.length - 1];
int n = keyColors.length;
// 映射到 [0, n-1]
float pos = v * (n - 1) / 255.0f;
int idx = (int) Math.floor(pos);
float t = pos - idx;
// 边界保护
if (idx >= n - 1) {
return keyColors[n - 1];
}
int c0 = keyColors[idx];
int c1 = keyColors[idx + 1];
int r0 = (c0 >> 16) & 0xFF;
int g0 = (c0 >> 8) & 0xFF;
int b0 = c0 & 0xFF;
int r1 = (c1 >> 16) & 0xFF;
int g1 = (c1 >> 8) & 0xFF;
int b1 = c1 & 0xFF;
int r = (int) (r0 + t * (r1 - r0));
int g = (int) (g0 + t * (g1 - g0));
int b = (int) (b0 + t * (b1 - b0));
return (r << 16) | (g << 8) | b;
}
private static void preStretch8Bit(Mat image, int channels) {
int totalValues = (int) image.total() * channels;
byte[] data = new byte[totalValues];
image.get(0, 0, data);
// min/max忽略 alpha
int min = 255, max = 0;
for (int i = 0; i < totalValues; i++) {
if (channels == 4 && (i % 4) == 3) continue; // 跳过 alpha
int v = data[i] & 0xFF;
if (v < min) min = v;
if (v > max) max = v;
}
if (max <= min) return; // 全黑或全白跳过
float scale = 255.0f / (max - min);
for (int i = 0; i < totalValues; i++) {
if (channels == 4 && (i % 4) == 3) continue; // alpha不变
int v = data[i] & 0xFF;
int val = Math.round((v - min) * scale);
data[i] = (byte) Math.min(255, Math.max(0, val));
}
image.put(0, 0, data);
}
/**
* 16bit 单通道/多通道预拉伸
*/
private static void preStretch16Bit(Mat image, int channels) {
int totalValues = (int) image.total() * channels;
short[] data = new short[totalValues];
image.get(0, 0, data);
int min = 65535, max = 0;
for (int i = 0; i < totalValues; i++) {
if (channels == 4 && (i % 4) == 3) continue;
int v = data[i] & 0xFFFF;
if (v < min) min = v;
if (v > max) max = v;
}
if (max <= min) return;
float scale = 65535.0f / (max - min);
for (int i = 0; i < totalValues; i++) {
if (channels == 4 && (i % 4) == 3) continue;
int v = data[i] & 0xFFFF;
int val = Math.round((v - min) * scale);
data[i] = (short) Math.min(65535, Math.max(0, val));
}
image.put(0, 0, data);
}
} }

View File

@ -4,6 +4,7 @@ window.config = {
api: 'http://127.0.0.1:9116', api: 'http://127.0.0.1:9116',
socket: 'http://127.0.0.1:9116', //外网服务器, socket: 'http://127.0.0.1:9116', //外网服务器,
imagePath: 'http://192.168.112.181:9000/files', imagePath: 'http://192.168.112.181:9000/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',

View File

@ -86,4 +86,7 @@ export const changePayloadRadarOpenState = params =>
export const changeJobStatus = params => export const changeJobStatus = params =>
req('post', '/sar/job/status/', params) req('post', '/sar/job/status', params)
export const execBrightnessexport = params =>
req('post', '/jm/job/exec/brightnessexport', params)

View File

@ -21,7 +21,7 @@ import {
getTaskItemDetail, getTaskItemDetail,
getTaskListData, getTaskListData,
getUavListData, getUavListData,
reRunTask, stopTaskFly, getExecJobs reRunTask, stopTaskFly, getExecJobs, execBrightnessexport
} from '@/api/task' } from '@/api/task'
import UavTarget from './uavTarget' import UavTarget from './uavTarget'
import PicturesUpload from '../pictures-upload/index.vue' import PicturesUpload from '../pictures-upload/index.vue'
@ -395,8 +395,9 @@ export default {
data: [] data: []
}, },
emptyImg: require('@/assets/img/common/empty.svg'), emptyImg: require('@/assets/img/common/empty.svg'),
lightPercent: 10, lightPercent: 0,
contrastPercent: 10 contrastPercent: 10,
imageInfos: undefined
} }
}, },
computed: { computed: {
@ -588,8 +589,13 @@ export default {
}, },
// 亮度设置变化 // 亮度设置变化
onLightChange: debounce(function (value) { onLightChange: debounce(function (value) {
const imageInfos = { ...this.imageInfos }
}, 200), imageInfos.brightness = value
execBrightnessexport(imageInfos).then(res => {
console.log('亮度调整成功');
// this.$message.success('亮度调整成功')
})
}, 500),
// startTest() { // startTest() {
// let testHeight = 1000 // let testHeight = 1000
// window.detectType = { // window.detectType = {
@ -2819,6 +2825,7 @@ export default {
handleWebsocketImage(info) { handleWebsocketImage(info) {
// console.log('SAR图像', info.body) // console.log('SAR图像', info.body)
let data = JSON.parse(info.body) let data = JSON.parse(info.body)
this.imageInfos = data
console.log('SAR图像2', data) console.log('SAR图像2', data)
this.addMarkPicture2(data) this.addMarkPicture2(data)
// console.log('灭有匹配到吗taskUavCollection', taskUavCollection, taskUavCollection[data.jobId]) // console.log('灭有匹配到吗taskUavCollection', taskUavCollection, taskUavCollection[data.jobId])

View File

@ -132,12 +132,16 @@
</div> </div>
</dt-card> </dt-card>
</left-slide> </left-slide>
<div class="image-set" v-if="taskList.visible && taskList.data.length"> <div
class="image-set"
v-if="taskList.visible && taskList.data.length && imageInfos"
>
<div class="image-set__item"> <div class="image-set__item">
<div class="is-label">亮度</div> <div class="is-label">亮度</div>
<el-slider <el-slider
v-model="lightPercent" v-model="lightPercent"
show-input show-input
max="30"
input-size="mini" input-size="mini"
:show-input-controls="false" :show-input-controls="false"
@input="onLightChange" @input="onLightChange"