%% 初始化
clc; clear; close all;
warning('off', 'images:imshow:all'); % 关闭所有 imshow 相关警告
% 定义路径和参数
inputFolder = 'C:\Users\pengxin916\Desktop\image';
outputFolder = 'processed_data';
targetSize = [512, 512]; % 统一分辨率
numGeneralAugment = 20; % 普通组合增强次数
numOrangeAugment = 100; % 橙绿组合增强次数
% 验证输出文件夹权限
if ~exist(outputFolder, 'dir')
mkdir(outputFolder);
end
% 创建组合文件夹
combinations = ["blue_green", "red_green", "orange_green"];
for combo = combinations
comboFolder = fullfile(outputFolder, char(combo));
if ~exist(comboFolder, 'dir')
mkdir(comboFolder);
end
end
%% 主处理流程
% 第一阶段:数据分类与预处理
fprintf('开始预处理阶段...\n');
% 获取输入文件夹中的所有图像文件
imageFiles = dir(fullfile(inputFolder, '*.jpg'));
numImages = numel(imageFiles);
fprintf('找到 %d 张图像\n', numImages);
for i = 1:numImages
try
% 读取图像文件
imgPath = fullfile(inputFolder, imageFiles(i).name);
img = imread(imgPath);
% 验证图像是否正确读取(确保为数值类型)
if ~isa(img, 'uint8') && ~isa(img, 'double')
fprintf("图像 %d/%d: 无法读取或格式不正确,跳过 (%s)\n", i, numImages, imageFiles(i).name);
continue;
end
% 统一尺寸
img = imresize(img, targetSize);
% 自动分类颜色组合
combo = detectColorCombo(img);
% 调试输出
if isempty(combo) || strcmp(combo, "unknown")
fprintf("图像 %d/%d: 未识别的颜色组合,跳过 (%s)\n", i, numImages, imageFiles(i).name);
% 显示颜色检测详情
[colorsFound, ~] = detectColorComboDebug(img);
fprintf(' 检测到的颜色: %s\n', join(colorsFound, ', '));
continue;
end
% 确保只处理预定义的颜色组合
if ~ismember(combo, combinations)
fprintf("图像 %d/%d: 不支持的颜色组合 %s,跳过\n", i, numImages, combo);
continue;
end
% 通道分离与配准
colors = split(combo, "_");
% 确保有两种颜色
if numel(colors) ~= 2
fprintf("图像 %d/%d: 颜色组合错误 (%s),跳过\n", i, numImages, combo);
continue;
end
% 使用圆括号索引字符串数组
color1 = colors(1);
color2 = colors(2);
% 提取颜色掩码
mask1 = getColorMask(img, color1);
mask2 = getColorMask(img, color2);
% 检查掩码有效性(提高阈值)
minPixels = 500; % 提高阈值以确保足够的像素
if nnz(mask1) < minPixels || nnz(mask2) < minPixels
fprintf("图像 %d/%d: 颜色掩码不完整,跳过\n", i, numImages);
% 显示掩码信息
fprintf(' %s 掩码像素: %d, %s 掩码像素: %d\n', color1, nnz(mask1), color2, nnz(mask2));
continue;
end
% 图像配准
try
% 使用改进的配准方法
[optimizer, metric] = imregconfig('multimodal');
optimizer.MaximumIterations = 100;
tform = imregtform(mask2, mask1, 'similarity', optimizer, metric);
aligned = imwarp(mask2, tform);
catch ME
fprintf("图像 %d/%d: 配准失败,使用默认变换 - %s\n", i, numImages, ME.message);
tform = affine2d(eye(3));
aligned = imwarp(mask2, tform);
end
% 保存预处理结果 - 确保保存为RGB图像
outputPath = fullfile(outputFolder, char(combo), sprintf("pre_%03d.png", i));
% 检查输出文件夹权限
[folderPart, ~, ~] = fileparts(outputPath);
if ~checkFolderWritable(folderPart)
fprintf("图像 %d/%d: 无法写入文件夹 %s,跳过\n", i, numImages, folderPart);
continue;
end
% 创建RGB图像:R通道为mask1, G通道为aligned, B通道为零
mask1_uint8 = uint8(mask1);
aligned_uint8 = uint8(aligned);
outputImg = cat(3, mask1_uint8, aligned_uint8, zeros(size(mask1_uint8)));
% 保存图像
imwrite(outputImg, outputPath);
fprintf("图像 %d/%d 预处理完成: %s\n", i, numImages, outputPath);
catch ME
fprintf("处理图像 %d/%d 时出错: %s\n", i, numImages, ME.message);
continue;
end
end
% 第二阶段:数据增强
fprintf('\n开始数据增强阶段...\n');
for combo = combinations
comboFolder = fullfile(outputFolder, char(combo));
% 检查是否有预处理图像
preprocessedFiles = dir(fullfile(comboFolder, 'pre_*.png'));
if isempty(preprocessedFiles)
fprintf('警告: 组合 "%s" 没有预处理图像,跳过增强\n', combo);
continue;
end
% 配置增强参数
if strcmp(combo, "orange_green")
augmenter = imageDataAugmenter( ...
"RandRotation", [-180, 180], ...
"RandXScale", [0.5, 2], ...
"RandYScale", [0.5, 2], ...
"RandXReflection", true, ...
"RandYReflection", true, ...
"RandXTranslation", [-20, 20], ...
"RandYTranslation", [-20, 20] ...
);
numAugment = numOrangeAugment;
else
augmenter = imageDataAugmenter( ...
"RandRotation", [-30, 30], ...
"RandXScale", [0.8, 1.2], ...
"RandYScale", [0.8, 1.2], ...
"RandXTranslation", [-10, 10], ...
"RandYTranslation", [-10, 10] ...
);
numAugment = numGeneralAugment;
end
fprintf('开始为组合 "%s" 生成增强图像,共有 %d 个预处理图像\n', combo, numel(preprocessedFiles));
% 为每个预处理图像生成增强图像
for i = 1:numel(preprocessedFiles)
imgName = preprocessedFiles(i).name;
imgPath = fullfile(comboFolder, imgName);
% 读取并验证图像
try
testImg = imread(imgPath);
% 确保图像为uint8类型
if ~isa(testImg, 'uint8')
testImg = im2uint8(testImg);
end
% 确保图像为三通道
if ndims(testImg) == 2
testImg = cat(3, testImg, testImg, testImg);
end
if ndims(testImg) ~= 3
fprintf("图像 %d/%d 维度不正确 (应为3),实际为 %d\n", i, numel(preprocessedFiles), ndims(testImg));
continue;
end
catch ME
fprintf("读取图像 %d/%d 时出错: %s\n", i, numel(preprocessedFiles), ME.message);
continue;
end
% 创建单图像的数据存储
singleImds = imageDatastore({imgPath}, 'IncludeSubfolders', false, 'FileExtensions', '.png');
% 创建增强数据存储
augDs = augmentedImageDatastore(targetSize, singleImds, "DataAugmentation", augmenter);
% 记录生成的增强图像数量
numGenerated = 0;
% 重置数据存储指针
reset(augDs);
% 循环生成增强图像
while numGenerated < numAugment && hasdata(augDs)
try
% 读取增强图像
augImg = read(augDs);
% 确保图像为uint8类型且维度正确
if ~isa(augImg, 'uint8')
augImg = im2uint8(augImg);
end
if ndims(augImg) == 2
augImg = cat(3, augImg, augImg, augImg);
end
% 生成输出文件名
[~, baseName, ~] = fileparts(imgName);
outputPath = fullfile(outputFolder, char(combo), sprintf("%s_aug%03d.png", baseName, numGenerated+1));
% 保存增强图像
imwrite(augImg, outputPath);
numGenerated = numGenerated + 1;
% 显示进度
if mod(numGenerated, 10) == 0
fprintf('%d%% ', round(numGenerated/numAugment*100));
end
catch ME
% 详细错误提示
fprintf('E 错误详情: %s\n', ME.message);
% 尝试诊断问题
try
% 检查数据存储内容
peekImg = read(augDs);
fprintf(' 数据存储内容类型: %s,维度: %s\n', class(peekImg), mat2str(size(peekImg)));
catch innerME
fprintf(' 无法读取数据存储: %s\n', innerME.message);
end
break; % 跳出循环,处理下一张图像
end
end
fprintf('完成 (%d/%d)\n', numGenerated, numAugment);
% 如果生成的增强图像数量不足,给出提示
if numGenerated < numAugment
fprintf(' 警告: 为图像 %d 只生成了 %d/%d 个增强图像\n', i, numGenerated, numAugment);
end
end
end
%% 自定义函数定义
function combo = detectColorCombo(img)
% 转换到HSV空间
hsv = rgb2hsv(img);
hue = hsv(:,:,1);
sat = hsv(:,:,2);
val = hsv(:,:,3);
% 提取有效像素(进一步降低阈值)
validPixels = hue(sat > 0.1 & val > 0.15); % 调整阈值
if isempty(validPixels)
combo = "unknown";
return;
end
% 统计主色分布
[counts, edges] = histcounts(validPixels, 0:0.025:1);
[~, idx] = sort(counts, 'descend');
idx = idx(1:min(5, numel(idx)));
% 颜色范围定义(进一步优化)
colorRanges = table( ...
{[0, 0.05]; [0.95, 1]; [0.05, 0.15]; [0.15, 0.3]; % 红色和橙色范围
[0.3, 0.5]; [0.5, 0.65]; [0.65, 0.85]; [0.85, 0.95]}, ... % 绿色、青色、蓝色、紫色范围
["red"; "red"; "orange"; "yellow"; "green"; "cyan"; "blue"; "purple"], ...
'VariableNames', {'Range', 'Color'} ...
);
mainColors = string.empty(0, 1);
for i = 1:numel(idx)
binCenter = (edges(idx(i)) + edges(idx(i)+1)) / 2;
for r = 1:height(colorRanges)
range = colorRanges.Range{r};
if (range(1) <= binCenter && binCenter <= range(2)) || ...
(range(1) > range(2) && (binCenter >= range(1) || binCenter <= range(2)))
mainColors(end+1) = colorRanges.Color(r);
break;
end
end
end
% 过滤颜色(保留更多颜色,只排除最不可能的)
validColors = mainColors(~ismember(mainColors, ["yellow", "cyan", "purple"]));
% 如果过滤后颜色不足,保留所有检测到的颜色
if numel(validColors) < 2
validColors = mainColors;
end
% 如果仍然不足,返回未知
if numel(validColors) < 2
combo = "unknown";
return;
end
uniqueColors = unique(validColors);
% 如果没有找到两种不同的颜色,尝试从重复颜色中选择
if numel(uniqueColors) < 2
combo = "unknown";
return;
end
% 优先考虑特定颜色组合
colorPair = sort(uniqueColors(1:2));
% 检查是否包含目标颜色组合
if ismember("red", colorPair) && ismember("green", colorPair)
combo = "red_green";
elseif ismember("orange", colorPair) && ismember("green", colorPair)
combo = "orange_green";
elseif ismember("blue", colorPair) && ismember("green", colorPair)
combo = "blue_green";
else
combo = "unknown";
end
end
% 调试版本的颜色检测函数,返回更多信息
function [colorsFound, details] = detectColorComboDebug(img)
% 此函数与detectColorCombo逻辑相同,但返回详细信息用于调试
hsv = rgb2hsv(img);
hue = hsv(:,:,1);
sat = hsv(:,:,2);
val = hsv(:,:,3);
validPixels = hue(sat > 0.1 & val > 0.15);
if isempty(validPixels)
colorsFound = [];
details = struct();
return;
end
[counts, edges] = histcounts(validPixels, 0:0.025:1);
[~, idx] = sort(counts, 'descend');
idx = idx(1:min(5, numel(idx)));
colorRanges = table( ...
{[0, 0.05]; [0.95, 1]; [0.05, 0.15]; [0.15, 0.3];
[0.3, 0.5]; [0.5, 0.65]; [0.65, 0.85]; [0.85, 0.95]}, ...
["red"; "red"; "orange"; "yellow"; "green"; "cyan"; "blue"; "purple"], ...
'VariableNames', {'Range', 'Color'} ...
);
mainColors = string.empty(0, 1);
for i = 1:numel(idx)
binCenter = (edges(idx(i)) + edges(idx(i)+1)) / 2;
for r = 1:height(colorRanges)
range = colorRanges.Range{r};
if (range(1) <= binCenter && binCenter <= range(2)) || ...
(range(1) > range(2) && (binCenter >= range(1) || binCenter <= range(2)))
mainColors(end+1) = colorRanges.Color(r);
break;
end
end
end
validColors = mainColors(~ismember(mainColors, ["yellow", "cyan", "purple"]));
if numel(validColors) < 2
validColors = mainColors;
end
colorsFound = validColors;
% 构建详细信息
details = struct();
details.numValidPixels = numel(validPixels);
details.mainColors = mainColors;
details.validColors = validColors;
details.hueDistribution = [edges(1:end-1); counts]';
end
function mask = getColorMask(img, color)
% 转换到LAB空间
lab = rgb2lab(img);
mask = false(size(img, 1), size(img, 2));
switch lower(color)
case "red"
% 优化红色检测
mask = ((lab(:,:,2) > 20 & lab(:,:,3) < 15) | (lab(:,:,2) > 30 & lab(:,:,3) > 20));
case "green"
% 优化绿色检测
mask = (lab(:,:,2) < -10 & lab(:,:,3) > 5);
case "blue"
% 优化蓝色检测
mask = (lab(:,:,3) < -20);
case "orange"
% 优化橙色检测
mask = (lab(:,:,2) > 15 & lab(:,:,3) > 20);
otherwise
error("未知颜色类型: %s", color);
end
% 形态学去噪
se = strel("disk", 3);
mask = imclose(imopen(mask, se), se);
mask = uint8(mask) * 255;
% 防止空掩码
if nnz(mask) == 0
mask = false(size(img,1), size(img,2));
end
end
function isWritable = checkFolderWritable(folderPath)
% 检查文件夹是否可写
isWritable = false;
if ~exist(folderPath, 'dir')
return;
end
% 尝试创建临时文件
try
tempFile = fullfile(folderPath, ".write_test_" + datetime('now', 'Format', 'yyyymmddHHMMSSFFF') + ".tmp");
fid = fopen(tempFile, 'w');
if fid ~= -1
fclose(fid);
delete(tempFile);
isWritable = true;
end
catch
% 忽略错误
end
end开始预处理阶段...
找到 26 张图像
图像 1/26: 未识别的颜色组合,跳过 (p1.jpg)
检测到的颜色: green, green, green, green, green
图像 2/26: 未识别的颜色组合,跳过 (p10.jpg)
检测到的颜色: red, red, red, red
图像 3/26: 未识别的颜色组合,跳过 (p11.jpg)
检测到的颜色: green, green, green, green
图像 4/26: 未识别的颜色组合,跳过 (p12.jpg)
检测到的颜色: red, red, red, red
图像 5/26: 未识别的颜色组合,跳过 (p13.jpg)
检测到的颜色: green, green, green, green, green
图像 6/26: 未识别的颜色组合,跳过 (p14.jpg)
检测到的颜色: red, red, red, red, orange
图像 7/26: 颜色掩码不完整,跳过
red 掩码像素: 0, green 掩码像素: 38388
图像 8/26: 未识别的颜色组合,跳过 (p16.jpg)
检测到的颜色: red, red, red, red, orange
图像 9/26: 未识别的颜色组合,跳过 (p17.jpg)
检测到的颜色: green, green, green
图像 10/26: 未识别的颜色组合,跳过 (p18.jpg)
检测到的颜色: red, red, red, red
图像 11/26: 未识别的颜色组合,跳过 (p19.jpg)
检测到的颜色: green, green
图像 12/26: 未识别的颜色组合,跳过 (p2.jpg)
检测到的颜色: cyan, cyan, cyan, cyan, blue
图像 13/26: 未识别的颜色组合,跳过 (p20.jpg)
检测到的颜色: cyan, cyan, cyan, cyan, cyan
图像 14/26: 未识别的颜色组合,跳过 (p21.jpg)
检测到的颜色: red, red, red, orange, red
图像 15/26: 未识别的颜色组合,跳过 (p22.jpg)
检测到的颜色: green, green, green, green
图像 16/26: 未识别的颜色组合,跳过 (p23.jpg)
检测到的颜色: blue, blue
图像 17/26: 未识别的颜色组合,跳过 (p24.jpg)
检测到的颜色: green, green, green, green
图像 18/26: 未识别的颜色组合,跳过 (p25.jpg)
检测到的颜色: green, green
图像 19/26: 未识别的颜色组合,跳过 (p26.jpg)
检测到的颜色: orange, orange, orange, orange, red
图像 20/26: 未识别的颜色组合,跳过 (p27.jpg)
检测到的颜色: green, green, green, green
图像 21/26: 未识别的颜色组合,跳过 (p28.jpg)
检测到的颜色: red, red, orange, red, orange
图像 22/26: 未识别的颜色组合,跳过 (p3.jpg)
检测到的颜色: green, green, green, green, green
图像 23/26: 未识别的颜色组合,跳过 (p4.jpg)
检测到的颜色: cyan, cyan, cyan, cyan, red
图像 24/26: 未识别的颜色组合,跳过 (p5.jpg)
检测到的颜色: blue, blue
图像 25/26: 未识别的颜色组合,跳过 (p6.jpg)
检测到的颜色: green, green, green
图像 26/26: 未识别的颜色组合,跳过 (p9.jpg)
检测到的颜色: green, green, green, green
开始数据增强阶段...
警告: 组合 "blue_green" 没有预处理图像,跳过增强
开始为组合 "red_green" 生成增强图像,共有 7 个预处理图像
E 错误详情: 第 1 个输入, Image, 应为以下类型之一:
double, logical, uint8, uint16, single, int16
但其类型是 table。
无法读取数据存储: 没有更多可读取的数据。请使用 reset 方法重置到数据的开头。请使用 hasdata 方法检查是否有可读取的数据。
完成 (0/20)
警告: 为图像 1 只生成了 0/20 个增强图像
E 错误详情: 第 1 个输入, Image, 应为以下类型之一:
double, logical, uint8, uint16, single, int16
但其类型是 table。
无法读取数据存储: 没有更多可读取的数据。请使用 reset 方法重置到数据的开头。请使用 hasdata 方法检查是否有可读取的数据。
完成 (0/20)
警告: 为图像 2 只生成了 0/20 个增强图像
E 错误详情: 第 1 个输入, Image, 应为以下类型之一:
double, logical, uint8, uint16, single, int16
但其类型是 table。
无法读取数据存储: 没有更多可读取的数据。请使用 reset 方法重置到数据的开头。请使用 hasdata 方法检查是否有可读取的数据。
完成 (0/20)
警告: 为图像 3 只生成了 0/20 个增强图像
E 错误详情: 第 1 个输入, Image, 应为以下类型之一:
double, logical, uint8, uint16, single, int16
但其类型是 table。
无法读取数据存储: 没有更多可读取的数据。请使用 reset 方法重置到数据的开头。请使用 hasdata 方法检查是否有可读取的数据。
完成 (0/20)
警告: 为图像 4 只生成了 0/20 个增强图像
E 错误详情: 第 1 个输入, Image, 应为以下类型之一:
double, logical, uint8, uint16, single, int16
但其类型是 table。
无法读取数据存储: 没有更多可读取的数据。请使用 reset 方法重置到数据的开头。请使用 hasdata 方法检查是否有可读取的数据。
完成 (0/20)
警告: 为图像 5 只生成了 0/20 个增强图像
E 错误详情: 第 1 个输入, Image, 应为以下类型之一:
double, logical, uint8, uint16, single, int16
但其类型是 table。
无法读取数据存储: 没有更多可读取的数据。请使用 reset 方法重置到数据的开头。请使用 hasdata 方法检查是否有可读取的数据。
完成 (0/20)
警告: 为图像 6 只生成了 0/20 个增强图像
E 错误详情: 第 1 个输入, Image, 应为以下类型之一:
double, logical, uint8, uint16, single, int16
但其类型是 table。
无法读取数据存储: 没有更多可读取的数据。请使用 reset 方法重置到数据的开头。请使用 hasdata 方法检查是否有可读取的数据。
完成 (0/20)
警告: 为图像 7 只生成了 0/20 个增强图像
警告: 组合 "orange_green" 没有预处理图像,跳过增强
>> 请提供一份完整的修改后且适用于matlab2023版本的代码