산업용 이미지를 활용한 이상탐지 모델 개선 (File Exchange)

Version 1.0.0 (3.69 KB) by 규민
1. 사전 준비 - 이미지가 저장된 파일 데이터 2. 데이터 증강 및 전처리
0 Downloads
Updated 30 Jul 2025

View License

  1. MATLAB knu.m 코드 (모델학습 후 성능보기 코드)
%% 1. 사전 준비
clc; clear;
% 사전학습된 ResNet-50 모델 불러오기!!
net = resnet50;
% 입력 이미지 크기 (224x224)를 변수로 저장
inputSize = net.Layers(1).InputSize(1:2);
%% 2. 데이터 로드 및 전처리
% 이미지가 저장된 폴더 경로를 지정 (각 하위 폴더 이름이 클래스 이름)
fullDatasetFolder = 'gray_normalized_images'; % 폴더 경로는 실제 구조에 맞게 변경해야 함
% 하위 폴더 이름을 기준으로 이미지와 레이블을 자동 생성
imds = imageDatastore(fullDatasetFolder, ...
'IncludeSubfolders', true, ...
'LabelSource', 'foldernames');
% ↓↓ 선택 사항: 불량 클래스를 하나로 통합하고 싶을 경우 사용 ↓↓
% defectiveClasses = ["bent", "flip", "scratch", "color"];
% imds.Labels(ismember(imds.Labels, defectiveClasses)) = "defective";
% imds.Labels = categorical(imds.Labels, {'good', 'defective'});
% 클래스별 이미지 수 확인
countEachLabel(imds)
% 학습용: 80%, 테스트용: 20%로 데이터 분할 (무작위 섞기)
[imdsTrain, imdsTest] = splitEachLabel(imds, 0.8, 'randomized');
% 이미지 크기를 ResNet-50 입력 크기(224x224)로 맞추고,
% 흑백(grayscale)을 RGB로 변환하여 모델 호환성 유지
augTrain = augmentedImageDatastore(inputSize, imdsTrain, ColorPreprocessing='gray2rgb');
augTest = augmentedImageDatastore(inputSize, imdsTest, ColorPreprocessing='gray2rgb');
%% 3. 전이학습을 위한 Layer 수정
% 기존 네트워크 구조를 가져와서 layerGraph 형태로 저장
lgraph = layerGraph(net);
% 분류할 클래스 수를 자동 계산
numClasses = numel(categories(imdsTrain.Labels));
% 새로 연결할 마지막 3개의 layer 정의 (fully connected + softmax + classification)
newLayers = [
fullyConnectedLayer(numClasses, 'Name', 'fc_new') % 분류 노드 개수 조정
softmaxLayer('Name','softmax_new') % Softmax 확률 계산
classificationLayer('Name','output') % 최종 분류 결과 출력
];
% 기존 레이어를 새 레이어로 교체
lgraph = replaceLayer(lgraph, 'fc1000', newLayers(1));
lgraph = replaceLayer(lgraph, 'fc1000_softmax', newLayers(2));
lgraph = replaceLayer(lgraph, 'ClassificationLayer_fc1000', newLayers(3));
%% 4. 학습 옵션 설정
options = trainingOptions('sgdm', ...
'InitialLearnRate',1e-4, ... % 학습률 설정
'MaxEpochs',8, ... % 전체 에폭 수 (8번 전체 학습)
'MiniBatchSize',16, ... % 미니 배치 크기
'Shuffle','every-epoch', ... % 매 에폭마다 셔플
'ValidationData', augTest, ... % 검증 데이터 지정
'ValidationFrequency', floor(numel(imdsTrain.Files)/16), ...% 몇 번마다 검증할지
'ValidationPatience', 5, ... % 성능 향상이 없을 경우 조기 종료
'Verbose',true, ... % 학습 중 정보 출력
'Plots','training-progress'); % 학습 상태 그래프로 시각화
% 앞쪽 10개와 커스터마이징된 마지막 3개 레이어만 추출해서 시각화 (단순 시각화용도)
lgraphSmall = lgraph.Layers([1:10 end-2:end]);
lgraphNew = layerGraph(lgraphSmall);
plot(lgraphNew)
%% 5. 네트워크 훈련
[trainedNet, trainInfo] = trainNetwork(augTrain, lgraph, options);
% save('', '';) %이것으로 save도 할수있음
%% 6. 테스트 및 예측
% 테스트 세트에 대해 예측 수행
predictedLabels = classify(trainedNet, augTest);
% 클래스별 확률값(점수) 추출
predScores = predict(trainedNet, augTest);
% 실제 라벨
trueLabels = imdsTest.Labels;
%% 7. 평가 지표 계산
% 각 클래스별 F1 Score 계산을 위한 준비
classes = categories(trueLabels);
f1_scores = zeros(numel(classes), 1);
% 클래스별로 Precision, Recall, F1 Score 수동 계산
for i = 1:numel(classes)
class = classes{i};
y_true = (trueLabels == class); % 실제 라벨
y_pred = (predictedLabels == class); % 예측 라벨
% 혼동행렬 요소 계산
TP = sum(y_true & y_pred); % True Positive
FP = sum(~y_true & y_pred); % False Positive
FN = sum(y_true & ~y_pred); % False Negative
% 정밀도, 재현율, F1 계산
precision = TP / (TP + FP + eps);
recall = TP / (TP + FN + eps);
f1_scores(i) = 2 * (precision * recall) / (precision + recall + eps);
fprintf('F1 Score for class %s: %.4f\n', class, f1_scores(i));
end
% 전체 평균 F1 점수
macroF1 = mean(f1_scores);
fprintf('Macro-average F1 Score: %.4f\n', macroF1);
% 혼동 행렬 시각화
figure;
confusionchart(trueLabels, predictedLabels);
%% ROC Curve 시각화 (모든 클래스에 대해 AUC 포함)
classNames = trainedNet.Layers(end).Classes;
numClasses = numel(classNames);
% 그래프 배치 계산 (자동 subplot)
numRows = ceil(sqrt(numClasses));
numCols = ceil(numClasses / numRows);
figure;
for i = 1:numClasses
currentClass = classNames(i);
% 클래스에 대한 ROC 커브 계산
[fpRate, tpRate, ~, auc] = perfcurve(trueLabels, predScores(:, i), currentClass);
% subplot으로 표시
subplot(numRows, numCols, i);
plot(fpRate, tpRate);
title(sprintf('ROC - %s (AUC = %.2f)', string(currentClass), auc));
xlabel('False Positive Rate');
ylabel('True Positive Rate');
grid on;
end
% 전체 타이틀
sgtitle('ROC Curves for All Classes');
%% 오분류된 결과 출력
% 잘못 예측된 인덱스 찾기
wrongIdx = find(predictedLabels ~= trueLabels);
% 전체 오분류 수 출력
fprintf('총 %d개의 잘못된 예측이 있습니다.\n', numel(wrongIdx));
% 오분류된 샘플의 예측/정답/경로 출력
for i = 1:numel(wrongIdx)
idx = wrongIdx(i);
predLabel = char(predictedLabels(idx));
trueLabel = char(trueLabels(idx));
imgPath = imdsTest.Files{idx};
fprintf('%3d - 예측: %-10s | 정답: %-10s | 파일: %s\n', ...
i, predLabel, trueLabel, imgPath);
end
2. 이미지 프로세싱 및 정규화 MATLAB코드 - augmentation.m
%% 데이터스토어 만들기
folderPath = 'C:\Users\docto\Desktop\매트랩\augmented_images';
bentPath = fullfile(folderPath, 'bent');
colorPath = fullfile(folderPath, 'color');
flipPath = fullfile(folderPath, 'flip');
goodPath = fullfile(folderPath, 'good');
scratchPath = fullfile(folderPath, 'scratch');
bentds = imageDatastore(bentPath, ...
'FileExtensions', '.png', ...
'IncludeSubfolders', false);
colords = imageDatastore(colorPath, ...
'FileExtensions', '.png', ...
'IncludeSubfolders', false);
flipds = imageDatastore(flipPath, ...
'FileExtensions', '.png', ...
'IncludeSubfolders', false);
goodds = imageDatastore(goodPath, ...
'FileExtensions', '.png', ...
'IncludeSubfolders', false);
scratchds = imageDatastore(scratchPath, ...
'FileExtensions', '.png', ...
'IncludeSubfolders', false);
%% 이미지 증강
augmenter = imageDataAugmenter( ...
'RandRotation', [-15, 15], ... % 회전 (15도 이내)
'RandScale', [0.9, 1.1], ... % 확대 및 축소 (10% 이내)
'RandXTranslation', [-10, 10], ... % 가로 이동 (픽셀값 10 이내)
'RandYTranslation', [-10, 10], ... % 세로 이동 (픽셀값 10 이내)
'RandXReflection', true, ... % 좌우 반전 (50% 확률)
'RandYReflection', true); % 상하 반전 (50% 확률)
mnds = {bentds, colords, flipds, goodds, scratchds};
classNames = {'bent', 'color', 'flip', 'good', 'scratch'};
%저장
outputFolder = 'C:\Users\docto\Desktop\final_images';
if ~exist(outputFolder, 'dir')
mkdir(outputFolder);
end
numAugPerImage = 30; % 원본 이미지당 증강 이미지 30개
for idx = 1:numel(mnds)
ds = mnds{idx};
auds = augmentedImageDatastore([224, 224], ds, 'DataAugmentation', augmenter);
reset(auds);
numFiles = numel(ds.Files);
totalAugImages = numFiles * numAugPerImage;
savedCount = 0;
while savedCount < totalAugImages
if ~hasdata(auds)
reset(auds);
end
data = read(auds);
batchSize = numel(data.input);
for j = 1:batchSize
img = data.input{j};
if iscell(img)
img = img{1};
end
% 밝기 변화: imadd로 랜덤 밝기 더하기 (-30 ~ +30)
delta = randi([-30, 30]); % 밝기 변화량
brightImg = imadd(img, delta, 'uint8');
savedCount = savedCount + 1;
filename = fullfile(outputFolder, ...
sprintf('%s_final_%03d.png', classNames{idx}, savedCount));
imwrite(brightImg, filename);
if savedCount >= totalAugImages
break;
end
end
end
end
%% RGB 이미지 정규화(min-max 정규화)
augmentedPath = 'C:\Users\docto\Desktop\final_images';
augmentedds = imageDatastore(augmentedPath, 'FileExtensions', '.png', 'IncludeSubfolders', false);
numFiles = numel(augmentedds.Files);
for i = 1:numFiles
% 이미지 읽기
img = readimage(augmentedds, i);
img_double = im2double(img);
% 이미지 내 최소, 최대값 계산
max_value = max(img_double(:));
min_value = min(img_double(:));
if max_value == min_value
img_normal = zeros(size(img_double));
else
% min-max 정규화
img_normal = (img_double - min_value) ./ (max_value - min_value);
end
[~, name, ext] = fileparts(augmentedds.Files{i});
outputFile = fullfile(outputFolder, [name '_normalized' ext]);
imwrite(img_normal, outputFile);
end
%% RGB 이미지 정규화 결과 확인 및 시각화
% 원본과 정규화 이미지 (각 채널별)
grayPath_52 = 'C:\Users\docto\Desktop\augmented_images';
filename_52 = 'color_augmented_052.png';
naugmentedPath_52 = 'C:\Users\docto\Desktop\augmented_images_normalized';
nfilename_52 = 'color_augmented_052_normalized.png';
filePath = fullfile(grayPath_52, filename_52);
nfilePath = fullfile(naugmentedPath_52, nfilename_52);
img = imread(filePath);
nimg = imread(nfilePath);
figure;
subplot(1,2,1);
imshow(img);
title('Original RGB image');
subplot(1,2,2);
imshow(nimg);
title('Rescaled RGB image');
img = im2double(imread(filePath));
nimg = im2double(imread(nfilePath));
% 히스토그램 비교 (각 채널별)
figure;
subplot(2,3,1);
imhist(img(:,:,1));
title('Original Red channel');
subplot(2,3,2);
imhist(img(:,:,2));
title('Original Green channel');
subplot(2,3,3);
imhist(img(:,:,3));
title('Original Blue channel');
subplot(2,3,4);
imhist(nimg(:,:,1));
title('Rescaled Red channel');
subplot(2,3,5);
imhist(nimg(:,:,2));
title('Rescaled Green channel');
subplot(2,3,6);
imhist(nimg(:,:,3));
title('Rescaled Blue channel');
% RGB 이미지 픽셀값 최대 최소
minVal = min(img(:));
maxVal = max(img(:));
nminVal = min(nimg(:));
nmaxVal = max(nimg(:));
fprintf('정규화 전 최소값: %.4f, 최대값: %.4f\n', minVal, maxVal);
fprintf('정규화 후 최소값: %.4f, 최대값: %.4f\n', nminVal, nmaxVal);
%% RGB 이미지 -> 흑백 이미지 (grayscale)
augmentedPath = 'C:\Users\docto\Desktop\augmented_images';
augmentedds = imageDatastore(augmentedPath, 'FileExtensions', '.png', 'IncludeSubfolders', false);
grayPath = 'C:\Users\docto\Desktop\grayscale_images'; % 저장할 흑백 이미지 폴더
if ~exist(grayPath, 'dir')
mkdir(grayPath);
end
% RGB → Grayscale 변환
numFiles = numel(augmentedds.Files);
for i = 1:numFiles
% 이미지 읽기
img = readimage(augmentedds, i);
% 그레이스케일 변환 (rgb2gray)
if size(img, 3) == 3 % RGB 이미지일 경우에만 변환
grayImg = rgb2gray(img);
else
grayImg = img; % 이미 그레이스케일 이미지면 그대로 사용
end
% 파일명 구성
[~, name, ext] = fileparts(augmentedds.Files{i});
filename = fullfile(grayPath, [name '_gray' ext]);
% 이미지 저장
imwrite(grayImg, filename);
end
%% 흑백 이미지 정규화(min-max 정규화)
grayds = imageDatastore(grayPath, 'FileExtensions', '.png', 'IncludeSubfolders', false);
outputFolder = 'C:\Users\docto\Desktop\gray_normalized_images';
if ~exist(outputFolder, 'dir')
mkdir(outputFolder);
end
numFiles = numel(grayds.Files);
for i = 1:numFiles
img = readimage(grayds, i);
img_double = im2double(img);
max_value = max(img_double(:));
min_value = min(img_double(:));
if max_value == min_value
img_normal = zeros(size(img_double));
else
img_normal = (img_double - min_value) ./ (max_value - min_value);
end
[~, name, ext] = fileparts(grayds.Files{i});
outputFile = fullfile(outputFolder, [name '_normalized' ext]);
imwrite(img_normal, outputFile);
end
%% 흑백 이미지 정규화 결과 확인 및 시각화
% 원본과 정규화 이미지
grayPath_52 = 'C:\Users\docto\Desktop\grayscale_images';
grayname_52 = 'color_augmented_052_gray.png';
ngrayPath_52 = 'C:\Users\docto\Desktop\gray_normalized_images';
ngrayname_52 = 'color_augmented_052_gray_normalized.png';
filePath = fullfile(grayPath_52, grayname_52);
nfilePath = fullfile(ngrayPath_52, ngrayname_52);
img = imread(filePath);
nimg = imread(nfilePath);
figure;
subplot(1,2,1);
imshow(img);
title('Original GRAY image');
subplot(1,2,2);
imshow(nimg);
title('Rescaled GRAY image');
img = im2double(imread(filePath));
nimg = im2double(imread(nfilePath));
% 히스토그램 비교
figure;
subplot(1,2,1);
imhist(img);
title('Original Grayscale Image');
subplot(1,2,2);
imhist(nimg);
title('Rescaled Grayscale Image');
% 흑백 이미지 픽셀값 최대 최소
minVal = min(img(:));
maxVal = max(img(:));
nminVal = min(nimg(:));
nmaxVal = max(nimg(:));
fprintf('정규화 전 최소값: %.4f, 최대값: %.4f\n', minVal, maxVal);
fprintf('정규화 후 최소값: %.4f, 최대값: %.4f\n', nminVal, nmaxVal);
ㅁㄴㅇ

Cite As

규민 (2025). 산업용 이미지를 활용한 이상탐지 모델 개선 (File Exchange) (https://www.mathworks.com/matlabcentral/fileexchange/181650-file-exchange), MATLAB Central File Exchange. Retrieved .

MATLAB Release Compatibility
Created with R2025a
Compatible with any release
Platform Compatibility
Windows macOS Linux

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!
Version Published Release Notes
1.0.0