java后端生成心电图-jfreechart

news2025/7/21 23:56:06

用jfreechart生成心电图

先上成功的图片

在这里插入图片描述

上代码

1.导入包

  implementation 'org.jfree:jfreechart:1.5.4'
  implementation 'org.jfree:jcommon:1.0.24'

2.实现代码

对数据进行滤波
转换单位
package com.shinrun.infrastructure.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class ECGDataConverter {
    
    /**
     * 将原始数据转换为mV单位的数据
     * @param rawData 原始数据数组
     * @param scaleFactor 比例因子(20000对应1mV)
     * @return 转换后的数据数组(单位mV)
     */
    public static double[] convertToMillivolts(double[] rawData, double scaleFactor) {
        double[] convertedData = new double[rawData.length];
        for (int i = 0; i < rawData.length; i++) {
            convertedData[i] = rawData[i] / scaleFactor;
        }
        return convertedData;
    }
    
    /**
     * 将原始数据转换为微伏(uV)单位的数据
     * @param rawData 原始数据数组
     * @param scaleFactor 比例因子(20000对应1mV)
     * @return 转换后的数据数组(单位uV)
     */
    public static double[] convertToMicrovolts(double[] rawData, double scaleFactor) {
        double[] convertedData = new double[rawData.length];
        for (int i = 0; i < rawData.length; i++) {
            convertedData[i] = (rawData[i] / scaleFactor) * 1000; // 转换为uV
        }
        return convertedData;
    }



    /**
     * 移动平均滤波器 - 平滑高频噪声
     * @param data 原始ECG数据(uV)
     * @param windowSize 窗口大小(建议3-7)
     * @return 滤波后的数据
     */
    public static double[] movingAverageFilter(double[] data, int windowSize) {
        if (windowSize < 1 || windowSize > data.length) {
            throw new IllegalArgumentException("Invalid window size");
        }

        double[] filtered = new double[data.length];
        int halfWindow = windowSize / 2;

        for (int i = 0; i < data.length; i++) {
            int start = Math.max(0, i - halfWindow);
            int end = Math.min(data.length - 1, i + halfWindow);

            double sum = 0;
            int count = 0;

            for (int j = start; j <= end; j++) {
                sum += data[j];
                count++;
            }

            filtered[i] = sum / count;
        }

        return filtered;
    }


    /**
     * 中值滤波器 - 有效去除脉冲噪声
     * @param data 原始ECG数据(uV)
     * @param windowSize 窗口大小(建议3-5)
     * @return 滤波后的数据
     */
    public static double[] medianFilter(double[] data, int windowSize) {
        if (windowSize < 1 || windowSize > data.length || windowSize % 2 == 0) {
            throw new IllegalArgumentException("Window size must be odd and positive");
        }

        double[] filtered = new double[data.length];
        int halfWindow = windowSize / 2;
        double[] window = new double[windowSize];

        for (int i = 0; i < data.length; i++) {
            int start = Math.max(0, i - halfWindow);
            int end = Math.min(data.length - 1, i + halfWindow);

            // 填充窗口
            int index = 0;
            for (int j = start; j <= end; j++) {
                window[index++] = data[j];
            }

            // 对窗口数据进行排序
            Arrays.sort(window, 0, index);

            // 取中值
            filtered[i] = window[index / 2];
        }

        return filtered;
    }


    /**
     * 基于阈值的异常值过滤
     * @param data 原始ECG数据(uV)
     * @param thresholdMultiplier 阈值乘数(建议2.5-3.5)
     * @return 过滤后的数据
     */
    public static double[] thresholdFilter(double[] data, double thresholdMultiplier) {
        // 计算数据的均值和标准差
        double mean = 0;
        for (double v : data) {
            mean += v;
        }
        mean /= data.length;

        double stdDev = 0;
        for (double v : data) {
            stdDev += Math.pow(v - mean, 2);
        }
        stdDev = Math.sqrt(stdDev / data.length);

        double threshold = thresholdMultiplier * stdDev;
        double[] filtered = new double[data.length];

        for (int i = 0; i < data.length; i++) {
            if (Math.abs(data[i] - mean) > threshold) {
                // 如果是异常值,用前后值的平均值替代
                double prev = i > 0 ? filtered[i-1] : mean;
                double next = i < data.length-1 ? data[i+1] : mean;
                filtered[i] = (prev + next) / 2;
            } else {
                filtered[i] = data[i];
            }
        }

        return filtered;
    }



    /**
     * 组合滤波器 - 先中值滤波再移动平均
     * @param data 原始ECG数据(uV)
     * @param medianWindow 中值滤波窗口大小(建议3)
     * @param averageWindow 移动平均窗口大小(建议5)
     * @return 滤波后的数据
     */
    public static double[] combinedFilter(double[] data, int medianWindow, int averageWindow) {
        double[] medianFiltered = medianFilter(data, medianWindow);
        return movingAverageFilter(medianFiltered, averageWindow);
    }



    /**
     * 原始数据强过滤(处理ADC值)
     * @param rawData 原始ADC数据
     * @param maxAllowed 允许的最大ADC绝对值
     * @return 过滤后的原始ADC数据
     */
    public static double[] filterRawData(double[] rawData, double maxAllowed) {
        if (rawData == null) {
          return new double[0];
        }

        double[] filtered = Arrays.copyOf(rawData, rawData.length);
        int windowSize = 7; // 使用前后各3个点(共7点窗口)

        for (int i = 0; i < filtered.length; i++) {
            if (Math.abs(filtered[i]) > maxAllowed) {
                // 获取周围正常值的滑动窗口中值
                filtered[i] = getSlidingWindowReplacement(filtered, i, windowSize, maxAllowed);
            }
        }
        return filtered;
    }

    private static double getSlidingWindowReplacement(double[] data, int centerIdx,
        int windowSize, double maxAllowed) {
        int halfWindow = windowSize / 2;
        List<Double> validValues = new ArrayList<>();

        // 收集窗口内有效值
        for (int i = -halfWindow; i <= halfWindow; i++) {
            int actualIdx = centerIdx + i;
            if (actualIdx >= 0 && actualIdx < data.length &&
                Math.abs(data[actualIdx]) <= maxAllowed) {
                validValues.add(data[actualIdx]);
            }
        }

        // 处理策略(按优先级):
        if (!validValues.isEmpty()) {
            // 1. 有有效值则取中位数
            Collections.sort(validValues);
            return validValues.get(validValues.size() / 2);
        } else {
            // 2. 无有效值则取前一个正常值(向前搜索)
            for (int i = centerIdx - 1; i >= 0; i--) {
                if (Math.abs(data[i]) <= maxAllowed) {
                  return data[i];
                }
            }
            // 3. 极端情况返回0
            return 0.0;
        }
    }

}

图片生成 方法
package com.shinrun.infrastructure.util;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.RectangleInsets;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;

/**
 * 医疗级心电图生成工具类(10段垂直排列)
 * 实现了标准心电图纸网格和比例尺
 */
public class ECGChartGenerator {

    // ============= 默认参数配置 =============
    private static final int DEFAULT_SAMPLE_RATE = 500; // 默认采样率500Hz
    private static final int SEGMENT_COUNT = 10; // 固定10段心电图
    private static final int DEFAULT_WIDTH = 800; // 图片默认宽度
    private static final int SEGMENT_HEIGHT = 150; // 每段心电图高度
    private static final int TOTAL_HEIGHT = SEGMENT_COUNT * SEGMENT_HEIGHT; // 总高度
    
    // 心电图纸网格参数(医疗标准)
    private static final int SMALL_GRID_MM = 1; // 小方格1mm
    private static final int LARGE_GRID_MM = 5; // 大方格5mm(5个小方格)
    private static final int GRID_SIZE_PX = 12; // 1mm对应的像素数(方法二)
    
    // 心电图显示参数
    private static final double DEFAULT_MM_PER_MV = 10.0; // 标准灵敏度:10mm/mV
    private static final double DEFAULT_PAPER_SPEED_MM_PER_SEC = 25.0; // 标准走纸速度:25mm/s

    private static final int DEFAULT_HEIGHT = 600; // 图片默认高度




    /**
     * 生成无标签的心电图纸网格
     */
    public static String generateECGGrid() throws IOException {
        // 创建空数据集(不需要实际数据)
        XYSeriesCollection dataset = new XYSeriesCollection();

        // 创建图表
        JFreeChart chart = ChartFactory.createXYLineChart(
            null, "", "", dataset);

        // 自定义网格样式
        customizeGridChart(chart);
        return convertChartToBase64(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT);
    }

    /**
     * 自定义网格图表样式
     */
    private static void customizeGridChart(JFreeChart chart) {
        XYPlot plot = chart.getXYPlot();

        // 1. 背景设置
        plot.setBackgroundPaint(Color.WHITE);
        chart.setBackgroundPaint(Color.WHITE);

        // 2. 网格线设置(医疗标准)
        plot.setDomainGridlinePaint(new Color(255, 200, 200)); // 小网格线颜色
        plot.setRangeGridlinePaint(new Color(255, 200, 200));
        plot.setDomainGridlineStroke(new BasicStroke(0.5f));   // 小网格线宽度
        plot.setRangeGridlineStroke(new BasicStroke(0.5f));

        // 计算网格大小(像素)
        double smallGridPx = SMALL_GRID_MM * GRID_SIZE_PX;
        double largeGridPx = LARGE_GRID_MM * GRID_SIZE_PX;

        // 3. X轴(时间轴)设置 - 隐藏所有标签和刻度
        NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
        xAxis.setAxisLineVisible(false);
        xAxis.setTickMarksVisible(false);
        xAxis.setTickLabelsVisible(false);
        xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        xAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));

        // 4. Y轴(幅度轴)设置 - 隐藏所有标签和刻度
        NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
        yAxis.setAxisLineVisible(false);
        yAxis.setTickMarksVisible(false);
        yAxis.setTickLabelsVisible(false);
        yAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));

        // 5. 移除所有渲染器(因为我们只需要网格)
        plot.setRenderer(null);

        // 6. 调整图表边距
        plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));

        // 7. 确保没有图例
        chart.removeLegend();

        // 8. 设置合适的范围以显示网格
        xAxis.setRange(0, DEFAULT_WIDTH / GRID_SIZE_PX);
        yAxis.setRange(0, DEFAULT_HEIGHT / GRID_SIZE_PX);
    }


    /**
     * 使用默认参数生成10段ECG Base64图片
     */
    public static String generateSegmentedECGImage(double[] ecgData) throws IOException {
        return generateSegmentedECGImage(ecgData, DEFAULT_SAMPLE_RATE, 
                                       DEFAULT_MM_PER_MV, DEFAULT_PAPER_SPEED_MM_PER_SEC);
    }

    /**
     * 自定义参数生成10段ECG Base64图片
     */
    public static String generateSegmentedECGImage(double[] ecgData, int sampleRate, 
                                                  double mmPerMv, double paperSpeedMmPerSec) throws IOException {
        // 计算每段数据点数
        int pointsPerSegment = ecgData.length / SEGMENT_COUNT;
        
        XYSeriesCollection dataset = new XYSeriesCollection();
        
        // 创建10个数据段
        for (int seg = 0; seg < SEGMENT_COUNT; seg++) {
            int start = seg * pointsPerSegment;
            int end = (seg == SEGMENT_COUNT - 1) ? ecgData.length : start + pointsPerSegment;
            
            XYSeries series = new XYSeries("ECG Segment " + (seg + 1));
            
            // 垂直偏移量(使各段分开显示)
            double yOffset = -seg * 2.0; // 每段下移2mV
            
            for (int i = start; i < end; i++) {
                // 计算时间并转换为毫米(基于走纸速度)
                double timeSec = (i - start) / (double) sampleRate;
                double xPosMm = timeSec * paperSpeedMmPerSec;
                // 转换为像素
                double xPixel = xPosMm * GRID_SIZE_PX;
                
                // 转换ECG值到mV(输入是uV)
                double valueMv = ecgData[i] / 1000.0;
                // 应用垂直比例尺(mm/mV)
                double yPosMm = valueMv * mmPerMv;
                // 转换为像素并加上偏移量
                double yPixel = yPosMm * GRID_SIZE_PX + (yOffset * mmPerMv * GRID_SIZE_PX);
                
                series.add(xPixel, yPixel);
            }
            dataset.addSeries(series);
        }

        // 创建图表
        JFreeChart chart = ChartFactory.createXYLineChart(
                null, "时间 (mm)", "幅度 (mm)", dataset);

        // 自定义心电图样式
        customizeMedicalECGChart(chart, mmPerMv, paperSpeedMmPerSec);
        return convertChartToBase64(chart, DEFAULT_WIDTH, TOTAL_HEIGHT);
    }

    /**
     * 自定义心电图图表样式(医疗标准)
     */
    private static void customizeMedicalECGChart(JFreeChart chart, double mmPerMv, double paperSpeedMmPerSec) {
        XYPlot plot = chart.getXYPlot();
        
        // 1. 背景设置
        plot.setBackgroundPaint(Color.WHITE);
        chart.setBackgroundPaint(Color.WHITE);
        
        // 2. 网格线设置(医疗标准)
        plot.setDomainGridlinePaint(new Color(255, 200, 200)); // 小网格线颜色
        plot.setRangeGridlinePaint(new Color(255, 200, 200));
        plot.setDomainGridlineStroke(new BasicStroke(0.5f));   // 小网格线宽度
        plot.setRangeGridlineStroke(new BasicStroke(0.5f));
        
        // 计算网格大小(像素)
        double smallGridPx = SMALL_GRID_MM * GRID_SIZE_PX;
        double largeGridPx = LARGE_GRID_MM * GRID_SIZE_PX;
        
        // 3. X轴(时间轴)设置
        NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
        xAxis.setAxisLinePaint(Color.BLACK);
        xAxis.setTickMarkPaint(Color.BLACK);
        xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        xAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));
        
        // 新版JFreeChart不再支持setMinorTickMarkPaint,改用以下方式设置次刻度
        xAxis.setMinorTickCount(4);
        xAxis.setMinorTickMarksVisible(true);
        
        // 4. Y轴(幅度轴)设置
        NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
        yAxis.setAxisLinePaint(Color.BLACK);
        yAxis.setTickMarkPaint(Color.BLACK);
        yAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));
        yAxis.setMinorTickCount(4);
        yAxis.setMinorTickMarksVisible(true);
        
        // 5. 心电图曲线渲染设置
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesPaint(0, Color.BLACK);
        renderer.setSeriesStroke(0, new BasicStroke(1.5f));
        
        // 为所有段应用相同的渲染器
        for (int i = 0; i < SEGMENT_COUNT; i++) {
            plot.setRenderer(i, renderer);
            renderer.setSeriesPaint(i, Color.BLACK);
            renderer.setSeriesStroke(i, new BasicStroke(1.5f));
            renderer.setSeriesShapesVisible(i, false);
        }
        
        // 6. 调整图表边距
        plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));
        
        // 7. 移除图例
        chart.removeLegend();
        
        // 8. 添加标题显示参数
        chart.setTitle("心电图 - " + mmPerMv + "mm/mV, 走纸速度 " + paperSpeedMmPerSec + "mm/s");
    }



    /**
     * 使用默认参数生成10段ECG Base64图片(无刻度值)
     */
    public static String generateSegmentedECGImageNoLabels(double[] ecgData) throws IOException {
        return generateSegmentedECGImageNoLabels(ecgData, DEFAULT_SAMPLE_RATE,
            DEFAULT_MM_PER_MV, DEFAULT_PAPER_SPEED_MM_PER_SEC);
    }


    /**
     * 自定义参数生成10段ECG Base64图片(无刻度值)
     */
    public static String generateSegmentedECGImageNoLabels(double[] ecgData, int sampleRate,
        double mmPerMv, double paperSpeedMmPerSec) throws IOException {
        // 计算每段数据点数
        int pointsPerSegment = ecgData.length / SEGMENT_COUNT;

        XYSeriesCollection dataset = new XYSeriesCollection();

        // 创建10个数据段
        for (int seg = 0; seg < SEGMENT_COUNT; seg++) {
            int start = seg * pointsPerSegment;
            int end = (seg == SEGMENT_COUNT - 1) ? ecgData.length : start + pointsPerSegment;

            XYSeries series = new XYSeries("ECG Segment " + (seg + 1));

            // 垂直偏移量(使各段分开显示)
            double yOffset = -seg * 2.0; // 每段下移2mV

            for (int i = start; i < end; i++) {
                // 计算时间并转换为毫米(基于走纸速度)
                double timeSec = (i - start) / (double) sampleRate;
                double xPosMm = timeSec * paperSpeedMmPerSec;
                // 转换为像素
                double xPixel = xPosMm * GRID_SIZE_PX;

                // 转换ECG值到mV(输入是uV)
                double valueMv = ecgData[i] / 1000.0;
                // 应用垂直比例尺(mm/mV)
                double yPosMm = valueMv * mmPerMv;
                // 转换为像素并加上偏移量
                double yPixel = yPosMm * GRID_SIZE_PX + (yOffset * mmPerMv * GRID_SIZE_PX);

                series.add(xPixel, yPixel);
            }
            dataset.addSeries(series);
        }

        // 创建图表
        JFreeChart chart = ChartFactory.createXYLineChart(
            null, "", "", dataset); // 空标题和空轴标签

        // 自定义心电图样式(无刻度值)
        customizeMedicalECGChartNoLabels(chart, mmPerMv, paperSpeedMmPerSec);
        return convertChartToBase64(chart, DEFAULT_WIDTH, TOTAL_HEIGHT);
    }


    /**
     * 自定义心电图图表样式(医疗标准,无刻度值)
     */
    private static void customizeMedicalECGChartNoLabels(JFreeChart chart, double mmPerMv, double paperSpeedMmPerSec) {
        XYPlot plot = chart.getXYPlot();

        // 1. 背景设置
        plot.setBackgroundPaint(Color.WHITE);
        chart.setBackgroundPaint(Color.WHITE);

        // 2. 网格线设置(医疗标准)
        plot.setDomainGridlinePaint(new Color(255, 200, 200)); // 小网格线颜色
        plot.setRangeGridlinePaint(new Color(255, 200, 200));
        plot.setDomainGridlineStroke(new BasicStroke(0.5f));   // 小网格线宽度
        plot.setRangeGridlineStroke(new BasicStroke(0.5f));

        // 计算网格大小(像素)
        double smallGridPx = SMALL_GRID_MM * GRID_SIZE_PX;
        double largeGridPx = LARGE_GRID_MM * GRID_SIZE_PX;

        // 3. X轴(时间轴)设置 - 隐藏刻度值
        NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
        xAxis.setAxisLineVisible(false);          // 隐藏轴线
        xAxis.setTickMarksVisible(false);         // 隐藏刻度线
        xAxis.setTickLabelsVisible(false);        // 隐藏刻度标签
        xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        xAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));

        // 4. Y轴(幅度轴)设置 - 隐藏刻度值
        NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
        yAxis.setAxisLineVisible(false);          // 隐藏轴线
        yAxis.setTickMarksVisible(false);         // 隐藏刻度线
        yAxis.setTickLabelsVisible(false);        // 隐藏刻度标签
        yAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));

        // 5. 心电图曲线渲染设置
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesPaint(0, Color.BLACK);
        renderer.setSeriesStroke(0, new BasicStroke(0.8f));

        // 为所有段应用相同的渲染器
        for (int i = 0; i < SEGMENT_COUNT; i++) {
            plot.setRenderer(i, renderer);
            renderer.setSeriesPaint(i, Color.BLACK);
            renderer.setSeriesStroke(i, new BasicStroke(0.8f));
            renderer.setSeriesShapesVisible(i, false);
        }

        // 6. 调整图表边距
        plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));

        // 7. 移除图例
        chart.removeLegend();

        // 8. 移除标题
        chart.setTitle("心电图 - " + mmPerMv + "mm/mV, 走纸速度 " + paperSpeedMmPerSec + "mm/s");
    }



    /**
     * 将图表转换为Base64编码的PNG图片
     */
    private static String convertChartToBase64(JFreeChart chart, int width, int height) throws IOException {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            ChartUtils.writeChartAsPNG(outputStream, chart, width, height);
            return "data:image/png;base64," + 
                   Base64.getEncoder().encodeToString(outputStream.toByteArray());
        }
    }
}

讲解使用

我的心电图数据是来着于第三方平台的(是由机器将心电数据文件上传到第三方系统,第三方系统给我返回的double数组)

首先是进行 滤波,数据会有很大的外界因素影响,波动会很大,
再将数据转换成mv
再画图,进行调试

  public String generateEcg(double[] rawData ) {

    // 转换参数
    double scaleFactor = 20.0;
    String oss="";

    // 方法1: 使用默认参数处理
    double[] processedECG = ECGDataConverter.filterRawData(rawData,4000);

    //转换成mv
    double[] doubles = ECGDataConverter.convertToMillivolts(processedECG, scaleFactor);
    // 生成时间轴
    try {
      String string = ECGChartGenerator.generateSegmentedECGImageNoLabels(doubles);

      oss= uploadFile(string, "image/ecg");
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    return oss;
  }
关键点
double[] processedECG = ECGDataConverter.filterRawData(rawData,4000);

这里 滤波参数我给的4000 其实就是 将超过4000的值给过滤掉,第三方给的数据 人的平均心跳峰值不超过4000,超过4000的那就不对需要过滤掉,这个根据你实际的数据进行修改

    double scaleFactor = 20.0;
  //转换成mv
    double[] doubles = ECGDataConverter.convertToMillivolts(processedECG, scaleFactor);

这里传个参数scaleFactor ,其实就是对数据进行除一下,这里由于第三方给的数据不太规范,不除以一下的话画出来不太好看
一般数据除到是百位数就行了

如果上面两点没有做好的话效果可能如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2398155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法/机理模型演示平台搭建(二)——算法接口部署(FastApi)

算法/机理模型演示平台搭建(二)—— 算法接口部署(FastApi) 1. 项目结构2. 构建 Docker 镜像3. 运行 Docker 容器4. 访问 API 文档5. 调用 API1. 项目结构 app app/algorithms app/models Dockerfile FROM python:3.9-slimWORKDIR /codeCOPY ./requirements.txt /code…

动态规划-647.回文子串-力扣(LeetCode)

一、题目解析 这里的子字符串是连续的&#xff0c;与之前的子序列不同&#xff0c;这里需要我们统计回文子串的数目。 二、算法原理 这里也有其他算法可以解决该问题&#xff0c;如中心扩展算法 时间复杂度O(N^2)/空间复杂度O(1)&#xff0c;马拉车算法(具有局限性) 时间复杂…

仿真每日一练 | Workbench中接触种类及选择方法简介

Workbench中给我们提供的接触类型主要包括以下几种&#x1f447; ◆ 1、摩擦 ◆ 2、无摩擦 ◆ 3、绑定 ◆ 4、不分离 ◆ 5、粗糙 ◆ 6、强制滑移 下面通过最常用的摩擦和绑定给大家展示两者的区别&#xff0c;同时文末也给大家介绍了几种接触的选择方法。首先先给大家介绍一下…

Go语言中的rune和byte类型详解

1. rune类型 1.1. 基本概念 1. rune是Go语言的内建类型&#xff0c;它是int32的别名&#xff0c;即32位有符号整数&#xff1b; 2. 用于表示一个Unicode码点&#xff0c;全拼Unicode code point&#xff1b; 3. 可以表示任何UTF-8编码的字符&#xff1b; 1.2. 特点 1. 每…

【java面试】redis篇

redis篇 一、适用场景&#xff08;一&#xff09;缓存1、缓存穿透1.1 解决方案1&#xff1a;缓存空数据&#xff0c;查询返回的数据为空&#xff0c;将空结果缓存1.2 解决方案2&#xff1a;布隆过滤器 2、缓存击穿1.1 解决方案1&#xff1a;互斥锁1.2 解决方案2&#xff1a;逻辑…

高效易用的 MAC 版 SVN 客户端:macSvn 使用体验

高效易用的 MAC 版 SVN 客户端&#xff1a;macSvn 使用体验 下载安装使用总结 最近有个项目要使用svn, 但是mac缺乏一款像 Windows 平台 TortoiseSVN 那样全面、高效且便捷的 SVN 客户端工具, 直到博主找到了该工具本文将结合实际使用体验&#xff0c;详细介绍 macSvn工具的核心…

HCIP(BGP综合实验)

一、实验拓扑 AS 划分&#xff1a; AS1&#xff1a;R1&#xff08;环回 L0:172.16.0.1/32&#xff0c;L1:192.168.1.0/24&#xff09;AS2&#xff1a;R2、R3、R4、R5、R6、R7&#xff08;内部运行 OSPF&#xff0c;AS 号为 64512 和 64513 的联盟&#xff09;AS3&#xff1a;R…

Attention Is All You Need (Transformer) 以及Transformer pytorch实现

参考https://zhuanlan.zhihu.com/p/569527564 Attention Is All You Need (Transformer) 是当今深度学习初学者必读的一篇论文。 一. Attention Is All You Need (Transformer) 论文精读 1. 知识准备 机器翻译&#xff0c;就是将某种语言的一段文字翻译成另一段文字。 由…

uniapp+vue2+uView项目学习知识点记录

持续更新中... 1、发送给朋友&#xff0c;分享到朋友圈功能开启 利用onShareAppMessage和onShareTimeline生命周期函数&#xff0c;在script中与data同级去写 // 发送给朋友 onShareAppMessage() {return {title: 清清前端, // 分享标题path: /pages/index/index, // 分享路…

精美的软件下载页面HTML源码:现代UI与动画效果的完美结合

精美的软件下载页面HTML源码&#xff1a;现代UI与动画效果的完美结合 在数字化产品推广中&#xff0c;一个设计精良的下载页面不仅能提升品牌专业度&#xff0c;还能显著提高用户转化率。本文介绍的精美软件下载页面HTML源码&#xff0c;通过现代化UI设计与丰富的动画效果&…

车载诊断架构 --- DTC消抖参数(Trip Counter DTCConfirmLimit )

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

javaEE->IO:

文件&#xff1a; 操作系统中会把很多 硬件设备 和 软件资源 抽象成“文件”&#xff0c;统一进行管理。 大部分谈到的文件&#xff0c;都是指 硬盘的文件&#xff0c;文件就相当于是针对“硬盘”数据的一种抽象 硬盘&#xff1a; 1.机械硬盘&#xff1a;便宜 2.固态硬盘&…

Oracle 用户/权限/角色管理

1. 用户 1.1. 用户的创建和删除 1.1.1. 创建用户 create user user identified {by password | externally} [ default tablespace tablespace ] [ temporary tablespace tablespace ] [ quota {integer [k | m ] | unlimited } on tablespace [ quota {integer [k | m ] | …

工厂方法模式深度解析:从原理到应用实战

作者简介 我是摘星&#xff0c;一名全栈开发者&#xff0c;专注 Java后端开发、AI工程化 与 云计算架构 领域&#xff0c;擅长Python技术栈。热衷于探索前沿技术&#xff0c;包括大模型应用、云原生解决方案及自动化工具开发。日常深耕技术实践&#xff0c;乐于分享实战经验与…

python可视化:端午假期旅游火爆原因分析

python可视化&#xff1a;端午假期旅游火爆原因分析 2025年的旅游市场表现强劲&#xff1a; 2025年端午假期全社会跨区域人员流动量累计6.57亿人次&#xff0c;日均2.19亿人次&#xff0c;同比增长3.0%。入境游订单同比大涨近90%&#xff0c;门票交易额&#xff08;GMV&#…

SOC-ESP32S3部分:28-BLE低功耗蓝牙

飞书文档https://x509p6c8to.feishu.cn/wiki/CHcowZMLtiinuBkRhExcZN7Ynmc 蓝牙是一种短距的无线通讯技术&#xff0c;可实现固定设备、移动设备之间的数据交换&#xff0c;下图是一个蓝牙应用的分层架构&#xff0c;Application部分则是我们需要实现的内容&#xff0c;Protoc…

Git-flow流

Git git是版本控制软件&#xff0c;一般用来做代码版本控制 github是一个免费版本控制仓库是国内外很多开源项目的集中地&#xff0c;其本体是一个git服务器 Git初始化操作 git init 初始化仓库 git status 查看当前仓库的状态 git add . 将改动的文件加到暂存区 gi…

VirtualBox给Rock Linux9.x配置网络

写这篇文章之前&#xff0c;先说明一下&#xff0c;我参考的是我之前写的《VirtualBox Linux网络配置》 我从CentOS7转到了Rock9&#xff0c;和配置Centos7一样&#xff0c;主流程没有变化&#xff0c;变化的是Rock9.x中的配置文件和使用的命令。 我再说一次&#xff0c;因为主…

知识图谱增强的大型语言模型编辑

https://arxiv.org/pdf/2402.13593 摘要 大型语言模型&#xff08;LLM&#xff09;是推进自然语言处理&#xff08;NLP&#xff09;任务的关键&#xff0c;但其效率受到不准确和过时知识的阻碍。模型编辑是解决这些挑战的一个有前途的解决方案。然而&#xff0c;现有的编辑方法…

.NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想

在当今数据驱动的时代&#xff0c;向量数据库&#xff08;Vector Database&#xff09;作为一种新兴的数据库技术&#xff0c;正逐渐成为软件开发领域的重要组成部分。特别是在 .NET 生态系统中&#xff0c;向量数据库的应用为开发者提供了构建智能、高效应用程序的新途径。 一…