PixelEdit使用教程

本文介绍PixelEdit这款像素图像编辑软件的使用方法,包括如何自定义图像尺寸、选择颜色及使用各种绘图工具,并通过实例演示如何高效创作像素图像。

这篇博文介绍像素图像的制作神器——PixelEdit,下载地址网上有很多,这里不贴出,大家自行搜索。安装打开后界面如图所示:

制作游戏素材之前,需要根据目标机器的分辨率,确定素材的长宽高的大小属性。图中左边的Size就是图像大小,默认有7种选择,通常,我们需要的不在这些列表,应该选择“custom”来自定义,点击后Width宽度和Height高度,以及Square正方形就解锁了,勾选Square,就是图像为正方形,长宽一致,改变其中之一,另一个也跟随改变。如果想要长宽不一致,应该取消这个Square选项,就可以自定义长和宽了。图中右边有两个可选框,Paste from clipboard,意思就是从剪贴板复制图像进来,如果你有对图像进行复制操作,勾选这个框就可以进一步设置细节了,如果没有,请不要随意勾选。Transparent background就是背景透明,如果不勾选,背景默认是白色,做出来的就是白底素材,通常,我们是需要勾选的。选好我们需要的,点击OK即可。

进入设计主界面,如图所示:

我用的64*64像素举例子,背景透明,颜色32位真彩色带可透明变化。如图所示,左边是主要的工具区,中间是绘图区,我们来看细节:

常用的就是这些,红色框内是我们用的画笔,可选择一个像素一个像素地填,也可以选择大面积大面积地填。此外,还可以输入像素文字(中英文皆可),输入文字默认位于左上角,拖动到相应位置即可。还有线条工具,可以绘制曲线,弧线和形状图案等,这些都比较简单。

黄色框内的是颜色工具,第一行,我们可以选取接下来会使用到的两种颜色,可以在绘制的时候方便互相切换,这个功能使用很方便,配合红色框的第二个吸管按钮可以很方便地将原图像还原成像素图案。在黄色框的底部,可以调节想要的灰度,透明度以及RGB值。

绿色框用的比较少,是做一些特效的,比如图像的渐变效果,绘制块的时候采用某种布局等等,这些用的少,一般就按软件打开的默认即可。

紫色框的功能比较强大,依次是左转90度,右转90度,左右对称,上下对称以及上下左右整体平移。这谢功能在制作的时候非常实用,比如说制作一个对称的物体,我们做好了左边部分,然后整体选中,复制粘贴,使用左右对称,拼接即可,使得工程量变小了,做起来很轻松。

利用上述工具,我做了一个例子,定时炸弹,采用的就是绘制一半加对称的模式,中间的文字使用文字输入。在我们制作完成后,请务必选择png格式输出,可以使像素图像无噪点,大部分的游戏工程都是用png格式图像,也便于软件的后期修改。

PixelEdit软件的使用非常简单,功能很强大,以上简要介绍了具体的使用,要制作一副精致的像素图像还需要大家的想象空间和动手能力,如有问题随时可留言探讨。

转载于:https://my.oschina.net/hqc17/blog/731429

Pixie Image Editor 在线图片编辑器 中文版 功能 集成 - 轻松将pixie集成到任何现有项目或应用程序中。 可扩展 - Pixie接口和API可以使用新功能进行扩展。 移动 - Pixie拥有完整的移动支持,并可自动调整其界面以适应任何设备的大小。 可自定义的UI - 通过显示,隐藏或添加新菜单项,更改工具栏位置或使用不同的主题来自定义UI。 可翻译 - Pixie的界面可通过配置完全翻译。 水印 - 保存的照片可以使用指定的文本轻松加水印。 模式 - 在叠加(模态),内联或全屏模式之间进行选择。 工具API - 通过API使用所有精灵工具(调整大小,裁剪,框架等),而无需打开精灵界面。 可自定义的工具 - 所有工具都可完全自定义,您可以删除或修改和添加自定义贴纸,形状,字体,框架等。 状态 - 以json格式保存当前编辑器状态,允许使用预构建模板等功能。 照片处理 - 通过界面或API调整大小,裁剪,转换等。 滤镜 - Pixie配有许多内置滤镜,如灰度,模糊,黑白,复古等。可以通过API添加更多过滤器.. 框架 - 为任何大小的照片添加内置响应帧或添加自己的帧。 裁剪 - 将照片裁剪为指定宽高比之一,或让用户通过UI选择自定义裁剪区域。 绘图 - 功能强大的免费绘图工具支持鼠标和触摸,具有多种画笔类型,颜色等。 文本 - 完全支持向图像添加文本。可以使用数百种谷歌字体或仅使用自定义添加的字体。 形状 - 只需指定svg图像路径,即可轻松添加自定义形状。 贴纸 - 可以添加或删除自定义贴纸。任何类型的图像都可以用作贴纸。 角落 - 只需单击一下或API调用即可对图像角进行四舍五入。 空画布 - Pixie不必编辑现有照片,也可以从头开始轻松创建自定义图像。 历史记录 - 所有编辑器操作都是非破坏性的,可以通过历史记录工具轻松撤消和重做。 对象 - 所有对象(如贴纸,形状和文本)都在自己的图层上,可以通过更改颜色,添加阴影,背景等来轻松移动,调整大小,删除和修改。 图案和渐变 - 所有对象都可以使用许多内置或自定义图案和渐变填充。 保存 - 修改后的图像可以通过API或接口轻松保存在本地设备或服务器上。 缩放和平移 - 可以使用鼠标,鼠标滚轮或移动设备上的触摸和捏合手势来缩放和平移画布。 HTML5 - Pixie使用原生HTML5,这意味着它可以在每个设备上运行。
%% 混凝土裂缝检测系统 - 完整单文件实现(增强版) classdef CrackDetectionSystem < handle properties % 模型参数 unetModel ganGenerator % GUI组件 appFig imgAxes resultAxes resultTable inputPathLabel outputPathLabel inputFolder = '' outputFolder = '' statusLabel % 系统参数 pixelSize = 0.1; % 每像素代表的实际尺寸(mm) confThreshold = 0.7; minCrackArea = 50; % 最小裂缝面积(像素) % 训练参数 trainDataDir = '' end methods function obj = CrackDetectionSystem() % 初始化系统 obj.createGUI(); obj.loadModels(); end function loadModels(obj) % 加载预训练模型 try % 加载U-Net裂缝分割模型 if exist('crack_unet.mat', 'file') unetData = load('crack_unet.mat'); obj.unetModel = unetData.net; else obj.unetModel = []; end % 加载GAN生成器模型 if exist('crack_gan_generator.mat', 'file') ganData = load('crack_gan_generator.mat'); obj.ganGenerator = ganData.genNet; else obj.ganGenerator = []; end obj.updateStatus('模型加载成功'); catch ME obj.updateStatus(sprintf('模型加载失败: %s', ME.message), 'error'); end end function createGUI(obj) % 创建主界面 obj.appFig = uifigure('Name', '混凝土裂缝检测系统', 'Position', [100 100 1400 850], ... 'CloseRequestFcn', @(src,event) obj.closeSystem()); % 图像显示区域 uilabel(obj.appFig, 'Text', '原始图像', 'Position', [50 800 100 20], 'FontSize', 12, 'FontWeight', 'bold'); obj.imgAxes = uiaxes(obj.appFig, 'Position', [50 500 600 300]); uilabel(obj.appFig, 'Text', '检测结果', 'Position', [700 800 100 20], 'FontSize', 12, 'FontWeight', 'bold'); obj.resultAxes = uiaxes(obj.appFig, '极', [700 500 600 300]); % 状态栏 obj.statusLabel = uilabel(obj.appFig, 'Text', '系统已就绪', 'Position', [50 20 1000 30], ... 'FontSize', 11, 'FontColor', [0 0.5 0]); % 文件夹选择区域 folderPanel = uipanel(obj.appFig, 'Title', '文件夹设置', 'Position', [50 400 600 80]); uilabel(folderPanel, 'Text', '输入文件夹:', 'Position', [20 40 80 20]); obj.inputPathLabel = uilabel(folderPanel, 'Text', '未选择', 'Position', [100 40 350 20], ... 'Interpreter', 'none'); uibutton(folderPanel, 'push', 'Text', '浏览...', 'Position', [470 40 60 20],... 'ButtonPushedFcn', @(btn,event) obj.selectInputFolder()); uilabel(folderPanel, 'Text', '输出文件夹:', 'Position', [20 10 80 20]); obj.outputPathLabel = uilabel(folder极, 'Text', '未选择', 'Position', [100 10 350 20], ... 'Interpreter', 'none'); uibutton(folderPanel, 'push', 'Text', '浏览...', 'Position', [470 10 60 20],... 'ButtonPushedFcn', @(btn,event) obj.selectOutputFolder()); % 参数设置区域 paramPanel = uipanel(obj.appFig, 'Title', '检测参数', 'Position', [700 400 600 80]); uilabel(paramPanel, 'Text', '像素尺寸(mm):', 'Position', [20 40 100 20]); pixelEdit = uieditfield(paramPanel, 'numeric', 'Position', [130 40 80 20],... 'Value', obj.pixelSize, 'ValueChangedFcn', @(src,event) obj.setPixelSize(src.Value)); uilabel(paramPanel, 'Text', '置信度阈值:', 'Position', [230 40 100 20]); confEdit = uieditfield(paramPanel, 'numeric', 'Position', [340 40 80 20],... 'Value', obj.confThreshold, 'Limits', [0.1 0.9], ... 'ValueChangedFcn', @(src,event) obj.setConfThreshold(src.Value)); uilabel(paramPanel, 'Text', '最小面积:', 'Position', [440 40 100 20]); areaEdit = uieditfield(paramPanel, 'numeric', 'Position', [540 40 50 20],... 'Value', obj.minCrackArea, 'ValueChangedFcn', @(src,event) obj.setMinCrackArea(src.Value)); % 模型训练区域 trainPanel = uipanel(obj.appFig, 'Title', '模型训练', 'Position', [50 300 600 80]); uilabel(trainPanel, 'Text', '训练数据:', 'Position', [20 40 80 20]); trainPathLabel = uilabel(trainPanel, 'Text', '未选择', 'Position', [100 40 350 20], ... 'Interpreter', 'none'); uibutton(trainPanel, 'push', 'Text', '浏览...', 'Position', [470 40 60 20],... 'ButtonPushedFcn', @(btn,event) obj.selectTrainFolder(trainPathLabel)); uibutton(trainPanel, 'push', 'Text', '训练U-Net', 'Position', [100 10 100 20],... 'ButtonPushedFcn', @(btn,event) obj.trainUnetModel()); uibutton(trainPanel, 'push', 'Text', '训练GAN', 'Position', [220 10 100 20],... 'ButtonPushedFcn', @(btn,event) obj.trainGANModel()); % 控制按钮 detectBtn = uibutton(obj.appFig, 'push', ... 'Text', '开始批量检测', ... 'Position', [800 350 150 30], ... 'FontSize', 12, ... 'FontWeight', 'bold', ... 'BackgroundColor', [0.2 0.6 1], ... 'FontColor', [1 1 1], ... 'ButtonPushedFcn', @(btn,event) obj.batchDetection()); evalBtn = uibutton(obj.appFig, 'push', ... 'Text', '评估系统', ... 'Position', [1000 350 150 30], ... 'FontSize', 12, ... 'FontWeight', 'bold', ... 'BackgroundColor', [0.2 0.8 0.2], ... 'FontColor', [1 1 1], ... 'ButtonPushedFcn', @(btn,event) obj.evaluateSystem()); % 结果表格 obj.resultTable = uitable(obj.appFig, 'Position', [50 50 1300 230],... 'ColumnName', {'文件名', '裂缝数量', '最大长度(mm)', '平均宽度(mm)', '最大宽度(mm)', '置信度', '状态'},... 'ColumnEditable', [false false false false false false false],... 'ColumnWidth', {'auto', 80, 100, 100, 100, 80, 100},... 'CellSelectionCallback', @(src,event) obj.showSelectedImage(event)); end function selectInputFolder(obj) % 选择输入文件夹 folder = uigetdir('', '选择输入图像文件夹'); if folder obj.inputFolder = folder; obj.inputPathLabel.Text = folder; obj.updateStatus(sprintf('输入文件夹: %s', folder)); end end function selectOutputFolder(obj) % 选择输出文件夹 folder = uigetdir('', '选择结果保存文件夹'); if folder obj.outputFolder = folder; obj.outputPathLabel.Text = folder; obj.updateStatus(sprintf('输出文件夹: %s', folder)); end end function selectTrainFolder(obj, labelHandle) % 选择训练数据文件夹 folder = uigetdir('', '选择训练数据集文件夹'); if folder obj.trainDataDir = folder; labelHandle.Text = folder; obj.updateStatus(sprintf('训练数据: %s', folder)); end end function setPixelSize(obj, size) % 设置像素尺寸 obj.pixelSize = size; obj.updateStatus(sprintf('像素尺寸设置为: %.4f mm/像素', size)); end function setConfThreshold(obj, threshold) % 设置置信度阈值 obj.confThreshold = threshold; obj.updateStatus(sprintf('置信度阈值设置为: %.2f', threshold)); end function setMinCrackArea(obj, area) % 设置最小裂缝面积 obj.minCrackArea = area; obj.updateStatus(sprintf('最小裂缝面积设置为: %d 像素', area)); end function updateStatus(obj, message, type) % 更新状态栏 if nargin < 3 type = 'info'; end switch type case 'info' color = [0 0.5 0]; % 绿色 case 'warning' color = [0.8 0.6 0]; % 黄色 case 'error' color = [0.8 0 0]; % 红色 otherwise color = [0 0 0]; % 黑色 end obj.statusLabel.Text = message; obj.statusLabel.FontColor = color; drawnow; end function batchDetection(obj) % 批量检测裂缝 if isempty(obj.inputFolder) || isempty(obj.outputFolder) obj.updateStatus('请先选择输入和输出文件夹', 'error'); return; end if isempty(obj.unetModel) obj.updateStatus('未加载U-Net模型,请先训练或加载模型', 'error'); return; end % 创建输出目录 resultDir = fullfile(obj.outputFolder, 'results'); maskDir = fullfile(obj.outputFolder, 'masks'); reportDir = fullfile(obj.outputFolder, 'reports'); if ~exist(resultDir, 'dir'), mkdir(resultDir); end if ~exist(maskDir, 'dir'), mkdir(maskDir); end if ~exist(reportDir, 'dir'), mkdir(reportDir); end % 获取图像文件 imageFiles = dir(fullfile(obj.inputFolder, '*.jpg')); imageFiles = [imageFiles; dir(fullfile(obj.inputFolder, '*.png'))]; imageFiles = [imageFiles; dir(fullfile(obj.inputFolder, '*.tif'))]; imageFiles = [imageFiles; dir(fullfile(obj.inputFolder, '*.bmp'))]; numImages = length(imageFiles); if numImages == 0 obj.updateStatus('输入文件夹中没有找到图像文件', 'error'); return; end % 初始化结果表格 results = cell(numImages, 7); summaryData = zeros(numImages, 4); % 裂缝数量, 最大长度, 平均宽度, 最大宽度 % 创建进度条 progressFig = uifigure('Name', '处理进度', 'Position', [500 500 400 150], ... 'CloseRequestFcn', @(src,event) setappdata(gcbf, 'cancelled', true)); setappdata(progressFig, 'cancelled', false); progressBar = uiprogressbar(progressFig, 'Position', [50 80 300 30]); progressLabel = uilabel(progressFig, 'Text', '准备开始...', 'Position', [50 50 300 20]); cancelBtn = uibutton(progressFig, 'push', 'Text', '取消', 'Position', [150 20 100 20],... 'ButtonPushedFcn', @(btn,event) setappdata(progressFig, 'cancelled', true)); % 处理每张图像 for i = 1:numImages % 检查是否取消 if getappdata(progressFig, 'cancelled') obj.updateStatus('用户取消了处理', 'warning'); break; end % 更新进度 progressBar.Value = i/numImages; progressLabel.Text = sprintf('处理 %d/%d: %s', i, numImages, imageFiles(i).name); drawnow; try % 读取图像 imgPath = fullfile(imageFiles(i).folder, imageFiles(i).name); img = imread(imgPath); % 显示原始图像 imshow(img, 'Parent', obj.imgAxes); title(obj.imgAxes, imageFiles(i).name, 'Interpreter', 'none'); % 使用GAN增强图像 enhancedImg = obj.enhanceWithGAN(img); % 裂缝检测 [mask, scores] = obj.detectCracks(enhancedImg); % 裂缝量化分析 [numCracks, maxLength, avgWidth, maxWidth] = obj.quantifyCracks(mask); % 转换为实际尺寸 maxLengthMM = maxLength * obj.pixelSize; avgWidthMM = avgWidth * obj.pixelSize; maxWidthMM = maxWidth * obj.pixelSize; % 保存结果 results(i,:) = {imageFiles(i).name, numCracks, maxLengthMM, avgWidthMM, maxWidthMM, mean(scores), '成功'}; summaryData(i,:) = [numCracks, maxLengthMM, avgWidthMM, maxWidthMM]; % 显示结果 overlayImg = labeloverlay(img, mask, 'Transparency', 0.7, ... 'Colormap', [0 0 0; 1 0 0]); % 背景黑色,裂缝红色 imshow(overlayImg, 'Parent', obj.resultAxes); title(obj.resultAxes, '检测结果'); % 保存检测结果 imwrite(overlayImg, fullfile(resultDir, ['result_' imageFiles(i).name])); imwrite(mask, fullfile(maskDir, ['mask_' imageFiles(i).name])); % 更新表格 obj.resultTable.Data = results; % 生成单个报告 obj.generateReport(imageFiles(i).name, img, mask, ... numCracks, maxLengthMM, avgWidthMM, maxWidthMM, ... fullfile(reportDir, ['report_' imageFiles(i).name(1:end-4) '.pdf'])); catch ME % 错误处理 results(i,:) = {imageFiles(i).name, NaN, NaN, NaN, NaN, NaN, sprintf('错误: %s', ME.message)}; obj.resultTable.Data = results; obj.updateStatus(sprintf('处理图像 %s 时出错: %s', imageFiles(i).name, ME.message), 'error'); end end % 保存结果表格 if isvalid(progressFig) && ~getappdata(progressFig, 'cancelled') % 保存Excel报告 resultsTable = cell2table(results, 'VariableNames', ... {'Filename', 'CrackCount', 'MaxLength_mm', 'AvgWidth_mm', 'MaxWidth_mm', 'Confidence', 'Status'}); writetable(resultsTable, fullfile(obj.outputFolder, 'detection_results.xlsx')); % 保存汇总统计 summaryStats = array2table(summaryData, 'VariableNames', ... {'CrackCount', 'MaxLength_mm', 'AvgWidth_mm', 'MaxWidth_m极'}); writetable(summaryStats, fullfile(obj.outputFolder, 'summary_statistics.xlsx')); % 生成PDF总结报告 obj.generateSummaryReport(results, fullfile(obj.outputFolder, 'summary_report.pdf')); progressLabel.Text = '处理完成!'; uibutton(progressFig, 'push', 'Text', '确定', 'Position', [150 20 100 20],... 'ButtonPushedFcn', @(btn,event) delete(progressFig)); obj.updateStatus(sprintf('成功处理 %d/%d 张图像', sum(strcmp(results(:,7), '成功')), numImages)); elseif isvalid(progressFig) delete(progressFig); end end function enhancedImg = enhanceWithGAN(obj, img) % GAN图像增强 if ~isempty(obj.ganGenerator) try % 预处理图像 inputSize = obj.ganGenerator.Layers(1).InputSize(1:2); resizedImg = imresize(img, inputSize); % 归一化到[-1,1]范围 if isinteger(resizedImg) resizedImg = im2single(resizedImg); end normalizedImg = (resizedImg - 0.5) * 2; % 通过生成器 if isa(obj.ganGenerator, 'dlnetwork') dlImg = dlarray(normalizedImg, 'SSCB'); dlEnhanced = predict(obj.ganGenerator, dlImg); enhancedImg = extractdata(dlEnhanced); else enhancedImg = predict(obj.ganGenerator, normalizedImg); end % 后处理 enhancedImg = (enhancedImg / 2) + 0.5; % 转换回[0,1]范围 enhancedImg = imresize(enhancedImg, [size(img,1), size(img,2)]); % 转换为原始数据类型 if isinteger(img) enhancedImg = im2uint8(enhancedImg); end catch ME obj.updateStatus(sprintf('GAN增强失败: %s,使用传统增强', ME.message), 'warning'); enhancedImg = obj.traditionalEnhancement(img); end else enhancedImg = obj.traditionalEnhancement(img); end end function img = traditionalEnhancement(obj, img) % 传统图像增强方法 if size(img, 3) == 3 img = rgb2gray(img); end % 自适应直方图均衡化 img = adapthisteq(img); % 对比度调整 img = imadjust(img); % 锐化 img = imsharpen(img, 'Amount', 2); end function [mask, scores] = detectCracks(obj, img) % 使用U-Net模型检测裂缝 inputSize = obj.unetModel.Layers(1).InputSize(1:2); resizedImg = imresize(img, inputSize); if isa(obj.unetModel, 'DAGNetwork') || isa(obj.unetModel, 'SeriesNetwork') [mask, scores] = semanticseg(resizedImg, obj.unetModel); else % 自定义网络预测 mask = semanticseg(resizedImg, obj.unetModel); scores = ones(size(mask)); % 默认置信度 end % 只保留裂缝类别 mask = mask == 'crack'; % 应用置信度阈值 if exist('scores', 'var') && ~isempty(scores) mask = mask & (scores(:,:,2) > obj.confThreshold); end % 后处理 mask = bwareaopen(mask, obj.minCrackArea); % 移除小区域 mask = imclose(mask, strel('disk', 3)); % 闭合小孔 % 恢复到原始尺寸 mask = imresize(mask, [size(img,1), size(img,2)], 'nearest'); end function [numCracks, maxLength, avgWidth, maxWidth] = quantifyCracks(obj, mask) % 量化裂缝参数 if ~any(mask(:)) numCracks = 0; maxLength = 0; avgWidth = 0; maxWidth = 0; return; end % 1. 连通区域分析 cc = bwconncomp(mask); numCracks = cc.NumObjects; % 2. 计算裂缝长度 lengths = zeros(1, numCracks); for i = 1:numCracks crackImg = false(size(mask)); crackImg(cc.PixelIdxList{i}) = true; % 骨架化计算长度 skel = bwskel(crackImg); lengths(i) = sum(skel(:)); end maxLength = max(lengths); % 3. 计算裂缝宽度 dist = bwdist(~mask); widths = 2 * dist(mask); avgWidth = mean(widths(widths > 0)); maxWidth = max(widths); end function generateReport(obj, filename, img, mask, numCracks, maxLength, avgWidth, maxWidth, outputPath) % 生成单个裂缝报告 fig = figure('Visible', 'off', 'Units', 'inches', 'Position', [0 0 8.5 11]); % 标题 annotation(fig, 'textbox', [0.1 0.9 0.8 0.1], 'String', '混凝土裂缝检测报告', ... 'FontSize', 20, 'FontWeight', 'bold', 'HorizontalAlignment', 'center', 'LineStyle', 'none'); % 图像信息 annotation(fig, 'textbox', [0.1 0.85 0.8 0.05], 'String', ['文件名: ' filename], ... 'FontSize', 12, 'HorizontalAlignment', 'left', 'LineStyle', 'none'); % 显示原始图像和结果 ax1 = axes('Position', [0.1 0.6 0.35 0.2]); imshow(img, 'Parent', ax1); title('原始图像'); ax2 = axes('Position', [0.55 0.6 0.35 0.2]); overlayImg = labeloverlay(img, mask, 'Transparency', 0.7, 'Colormap', [0 0 0; 1 0 0]); imshow(overlayImg, 'Parent', ax2); title('检测结果'); % 量化结果 resultsText = {... sprintf('裂缝数量: %d', numCracks), ... sprintf('最大长度: %.2f mm', maxLength), ... sprintf('平均宽度: %.2f mm', avgWidth), ... sprintf('最大宽度: %.2f mm', maxWidth)}; annotation(fig, 'textbox', [0.1 0.4 0.8 0.15], 'String', resultsText, ... 'FontSize', 14, 'FontWeight', 'bold', 'LineStyle', 'none'); % 裂缝属性分布 ax3 = axes('Position', [0.1 0.1 0.8 0.25]); if numCracks > 0 % 计算每个裂缝的属性 cc = bwconncomp(mask); lengths = zeros(1, cc.NumObjects); widths = zeros(1, cc.NumObjects); for i = 1:cc.NumObjects crackImg = false(size(mask)); crackImg(cc.PixelIdxList{i}) = true; % 长度 skel = bwskel(crackImg); lengths(i) = sum(skel(:)) * obj.pixelSize; % 宽度 dist = bwdist(~crackImg); crackWidths = 2 * dist(crackImg); widths(i) = mean(crackWidths(crackWidths > 0)) * obj.pixelSize; end % 绘制分布 yyaxis left; bar(1:numCracks, lengths); ylabel('长度 (mm)'); yyaxis right; plot(1:numCracks, widths, 'ro-', 'MarkerSize', 8, 'LineWidth', 2); ylabel('平均宽度 (mm)'); xlabel('裂缝编号'); title('裂缝属性分布'); legend('长度', '宽度', 'Location', 'best'); grid on; else text(0.5, 0.5, '未检测到裂缝', 'HorizontalAlignment', 'center', 'FontSize', 14); axis off; end % 保存为PDF exportgraphics(fig, outputPath, 'ContentType', 'vector'); close(fig); end function generateSummaryReport(obj, results, outputPath) % 生成总结报告 fig = figure('Visible', 'off', 'Units', 'inches', 'Position', [0 0 8.5 11]); % 标题 annotation(fig, 'textbox', [0.1 0.95 0.8 0.05], 'String', '混凝土裂缝检测总结报告', ... 'FontSize', 20, 'FontWeight', 'bold', 'HorizontalAlignment', 'center', 'LineStyle', 'none'); % 基本信息 infoText = {... sprintf('检测时间: %s', datestr(now)), ... sprintf('输入文件夹: %s', obj.inputFolder), ... sprintf('输出文件夹: %s', obj.outputFolder), ... sprintf('处理图像数量: %d', size(results, 1)), ... sprintf('检测到裂缝的图像数量: %d', sum(cell2mat(results(:,2)) > 0))}; annotation(fig, 'textbox', [0.1 0.85 0.8 0.08], 'String', infoText, ... 'FontSize', 12, 'LineStyle', 'none'); % 提取数值数据 validIdx = cellfun(@(x) isscalar(x) && ~isnan(x), results(:,2)); crackCounts = cell2mat(results(validIdx,2)); maxLengths = cell2mat(results(validIdx,3)); avgWidths = cell2mat(results(validIdx,4)); maxWidths = cell2mat(results(validIdx,5)); % 统计图表 ax1 = subplot(2,2,1, 'Parent', fig); histogram(ax1, crackCounts, 'BinMethod', 'integers'); title(ax1, '裂缝数量分布'); xlabel(ax1, '裂缝数量'); ylabel(ax1, '图像数量'); grid(ax1, 'on'); ax2 = subplot(2,2,2, 'Parent', fig); histogram(ax2, maxLengths, 20); title(ax2, '最大长度分布'); xlabel(ax2, '最大长度 (mm)'); ylabel(ax2, '图像数量'); grid(ax2, 'on'); ax3 = subplot(2,2,3, 'Parent', fig); histogram(ax3, avgWidths, 20); title(ax3, '平均宽度分布'); xlabel(ax3, '平均宽度 (mm)'); ylabel(ax3, '图像数量'); grid(ax3, 'on'); ax4 = subplot(2,2,4, 'Parent', fig); histogram(ax4, maxWidths, 20); title(ax4, '最大宽度分布'); xlabel(ax4, '最大宽度 (mm)'); ylabel(ax4, '图像数量'); grid(ax4, 'on'); % 保存为PDF exportgraphics(fig, outputPath, 'ContentType', 'vector'); close(fig); end function showSelectedImage(obj, event) % 显示选中的图像 if ~isempty(event.Indices) row = event.Indices(1); data = obj.resultTable.Data; filename = data{row,1}; if ~isempty(obj.inputFolder) imgPath = fullfile(obj.inputFolder, filename); if exist(imgPath, 'file') img = imread(imgPath); imshow(img, 'Parent', obj.imgAxes); title(obj.imgAxes, filename, 'Interpreter', 'none'); % 尝试显示结果 resultPath = fullfile(obj.outputFolder, 'results', ['result_' filename]); if exist(resultPath, 'file') resultImg = imread(resultPath); imshow(resultImg, 'Parent', obj.resultAxes); title(obj.resultAxes, '检测结果'); end end end end end function evaluateSystem(obj) % 系统评估 testDir = uigetdir('', '选择测试数据集目录'); if testDir == 0 return; % 用户取消 end % 加载测试数据 imgDir = fullfile(testDir, 'images'); maskDir = fullfile(testDir, 'masks'); imgFiles = dir(fullfile(imgDir, '*.jpg')); imgFiles = [imgFiles; dir(fullfile(imgDir, '*.png'))]; maskFiles = dir(fullfile(maskDir, '*.png')); if numel(imgFiles) ~= numel(maskFiles) errordlg('测试图像和掩码数量不匹配', '数据错误'); return; end % 初始化指标 totalTP = 0; totalFP = 0; totalFN = 0; lengthErrors = []; widthErrors = []; detectionTimes = []; % 进度条 progressFig = uifigure('Name', '系统评估', 'Position', [500 500 400 150]); progressBar = uiprogressbar(progressFig, 'Position', [50 80 300 30]); progressLabel = uilabel(progressFig, 'Text', '开始评估...', 'Position', [50 50 300 20]); % 处理每个样本 for i = 1:numel(imgFiles) progressBar.Value = i/numel(imgFiles); progressLabel.Text = sprintf('评估 %d/%d: %s', i, numel(imgFiles), imgFiles(i).name); drawnow; try % 读取图像和真实掩码 img = imread(fullfile(imgDir, imgFiles(i).name)); trueMask = imread(fullfile(maskDir, maskFiles(i).name)); trueMask = imbinarize(trueMask); % 计时 tic; % 增强和检测 enhancedImg = obj.enhanceWithGAN(img); [predMask, ~] = obj.detectCracks(enhancedImg); detectionTimes(end+1) = toc; % 计算分割指标 tp = sum(predMask & trueMask, 'all'); fp = sum(predMask & ~trueMask, 'all'); fn = sum(~predMask & trueMask, 'all'); totalTP = totalTP + tp; totalFP = totalFP + fp; totalFN = totalFN + fn; % 计算量化指标 if any(trueMask(:)) [~, trueMaxLength, trueAvgWidth] = obj.quantifyCracks(trueMask); [~, predMaxLength, predAvgWidth] = obj.quantifyCracks(predMask); lengthErrors = [lengthErrors, abs(trueMaxLength - predMaxLength) * obj.pixelSize]; widthErrors = [widthErrors, abs(trueAvgWidth - predAvgWidth) * obj.pixelSize]; end catch ME warning('评估样本 %s 时出错: %s', imgFiles(i).name, ME.message); end end % 计算指标 precision = totalTP / (totalTP + totalFP); recall = totalTP / (totalTP + totalFN); f1Score = 2 * (precision * recall) / (precision + recall); meanLengthError = mean(lengthErrors); meanWidthError = mean(widthErrors); meanDetectionTime = mean(detectionTimes); % 显示结果 resultText = sprintf(['系统评估结果:\n'... '精确率: %.4f\n'... '召回率: %.4f\n'... 'F1分数: %.4f\n'... '平均长度误差: %.2f mm\n'... '平均宽度误差: %.2f mm\n'... '平均检测时间: %.2f 秒'], ... precision, recall, f1Score, meanLengthError, meanWidthError, meanDetectionTime); % 保存评估报告 evalReport = fullfile(obj.outputFolder, 'evaluation_report.txt'); fid = fopen(evalReport, 'w'); fprintf(fid, '混凝土裂缝检测系统评估报告\n'); fprintf(fid, '评估时间: %s\n\n', datestr(now)); fprintf(fid, '性能指标:\n'); fprintf(fid, '精确率: %.4f\n', precision); fprintf(fid, '召回率: %.4f\n', recall); fprintf(fid, 'F1分数: %.4f\n', f1Score); fprintf(fid, '平均长度误差: %.4f mm\n', meanLengthError); fprintf(fid, '平均宽度误差: %.4f mm\n', meanWidthError); fprintf(fid, '平均检测时间: %.4f 秒\n', meanDetectionTime); fclose(fid); msgbox(resultText, '评估结果'); close(progressFig); obj.updateStatus(sprintf('评估完成! F1分数: %.2f', f1Score)); end function trainUnetModel(obj) % 训练U-Net模型 if isempty(obj.trainDataDir) obj.updateStatus('请先选择训练数据集', 'error'); return; end obj.updateStatus('开始训练U-Net模型...'); try % 准备数据集 imds = imageDatastore(fullfile(obj.trainDataDir, 'images')); pxds = pixelLabelDatastore(fullfile(obj.trainDataDir, 'masks'), {'background', 'crack'}, [0 1]); % 划分训练验证集 [imdsTrain, imdsVal, pxdsTrain, pxdsVal] = partitionDataset(imds, pxds, 0.8); % 创建U-Net架构 inputSize = [256 256 3]; numClasses = 2; lgraph = unetLayers(inputSize, numClasses); % 数据增强 augmenter = imageDataAugmenter(... 'RandXReflection', true, ... 'RandYReflection', true, ... 'RandRotation', [-30 30], ... 'RandXTranslation', [-30 30], ... 'RandYTranslation', [-30 30]); pximds = pixelLabelImageDatastore(imdsTrain, pxdsTrain, ... 'DataAugmentation', augmenter, ... 'OutputSize', inputSize(1:2)); % 训练选项 options = trainingOptions('adam', ... 'InitialLearnRate', 1e-3, ... 'MaxEpochs', 50, ... 'MiniBatchSize', 8, ... 'ValidationData', {imdsVal, pxdsVal}, ... 'ValidationFrequency', 100, ... 'Plots', 'training-progress', ... 'OutputFcn', @(info) obj.trainingUpdate(info), ... 'ExecutionEnvironment', 'gpu'); % 训练模型 [net, info] = trainNetwork(pximds, lgraph, options); % 保存模型 save('crack_unet.mat', 'net'); obj.unetModel = net; obj.updateStatus('U-Net模型训练完成并保存'); catch ME obj.updateStatus(sprintf('训练失败: %s', ME.message), 'error'); end end function trainGANModel(obj) % 训练GAN模型 if isempty(obj.trainDataDir) obj.updateStatus('请先选择训练数据集', 'error'); return; end obj.updateStatus('开始训练GAN模型...'); try % 加载图像数据集 imds = imageDatastore(fullfile(obj.trainDataDir, 'images'), ... 'IncludeSubfolders', true, ... 'LabelSource', 'foldernames'); % 调整图像大小 imds = augmentedImageDatastore([256 256], imds, 'ColorPreprocessing', 'gray2rgb'); % 定义生成器 generator = [ imageInputLayer([1 1 100], 'Normalization', 'none', 'Name', 'in') transposedConv2dLayer(4, 512, 'Name', 'tconv1') batchNormalizationLayer('Name', 'bn1') reluLayer('Name', 'relu1') transposedConv2dLayer(4, 256, 'Stride', 2, 'Cropping', 1, 'Name', 'tconv2') batchNormalizationLayer('Name', 'bn2') reluLayer('Name', 'relu2') transposedConv2dLayer(4, 128, 'Stride', 2, 'Cropping', 1, 'Name', 'tconv3') batchNormalizationLayer('Name', 'bn3') reluLayer('Name', 'relu3') transposedConv2dLayer(4, 64, 'Stride', 2, 'Cropping', 1, 'Name', 'tconv4') batchNormalizationLayer('Name', 'bn4') reluLayer('Name', 'relu4') transposedConv2dLayer(4, 3, 'Stride', 2, 'Cropping', 1, 'Name', 'tconv5') tanhLayer('Name', 'tanh')]; % 定义判别器 discriminator = [ imageInputLayer([256 256 3], 'Normalization', 'none', 'Name', 'in') convolution2dLayer(4, 64, 'Stride', 2, 'Padding', 1, 'Name', 'conv1') leakyReluLayer(0.2, 'Name', 'lrelu1') convolution2dLayer(4, 128, 'Stride', 2, 'Padding', 1, 'Name', 'conv2') batchNormalizationLayer('Name', 'bn1') leakyReluLayer(0.2, 'Name', 'lrelu2') convolution2dLayer(4, 256, 'Stride', 2, 'Padding', 1, 'Name', 'conv3') batchNormalizationLayer('Name', 'bn2') leakyReluLayer(0.2, 'Name', 'lrelu3') convolution2dLayer(4, 512, 'Stride', 2, 'Padding', 1, 'Name', 'conv4') batchNormalizationLayer('Name', 'bn3') leakyReluLayer(0.2, 'Name', 'lrelu4') convolution2dLayer(4, 1, 'Name', 'conv5') sigmoidLayer('Name', 'sigmoid')]; % 创建GAN gan = gan(generator, discriminator); % 训练选项 options = trainingOptions('adam', ... 'MaxEpochs', 200, ... 'MiniBatchSize', 16, ... 'Plots', 'training-progress', ... 'OutputFcn', @(info) obj.trainingUpdate(info), ... 'ExecutionEnvironment', 'gpu'); % 训练GAN [gan, info] = trainGAN(gan, imds, options); % 保存生成器 genNet = gan.Generator; save('crack_gan_generator.mat', 'genNet'); obj.ganGenerator = genNet; obj.updateStatus('GAN模型训练完成并保存'); catch ME obj.updateStatus(sprintf('训练失败: %s', ME.message), 'error'); end end function stop = trainingUpdate(~, info) % 训练进度更新函数 stop = false; if info.State == "iteration" fprintf('Epoch %d, Iteration %d, Loss: %.4f\n', ... info.Epoch, info.Iteration, info.TrainingLoss); end end function closeSystem(obj) % 关闭系统 delete(obj.appFig); end end end %% 辅助函数 function [imdsTrain, imdsVal, pxdsTrain, pxdsVal] = partitionDataset(imds, pxds, splitRatio) % 随机划分数据集 numFiles = numel(imds.Files); shuffledIndices = randperm(numFiles); trainSize = round(splitRatio * numFiles); trainIndices = shuffledIndices(1:trainSize); valIndices = shuffledIndices(trainSize+1:end); imdsTrain = subset(imds, trainIndices); imdsVal = subset(imds, valIndices); pxdsTrain = subset(pxds, trainIndices); pxdsVal = subset(pxds, valIndices); end %% 系统启动函数 function runCrackDetectionSystem() % 创建系统实例 crackSystem = CrackDetectionSystem(); end 修改
最新发布
08-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值