自行拍摄鸡蛋破损照片,简述拍摄照片时应注意的事项(包括相机角度,背景设置,光线环境,拍摄距离等);简述图片预处理和瑕疵检测时的考虑与方案设计。
1.拍照注意事项 ①相机角度尽量在鸡蛋的正上方约15cm处; ②调整镜头,使鸡蛋位于拍摄图像中心; ③背景颜色为黑色; ④背景需展平且干净,不可有褶皱或其他干扰色点; ⑤不宜在灯光正下方拍摄,易存在阴影; ⑥在光源照射下,不要使背景过亮或过暗,拍摄时易反光或无区分度; 2. 算法大致步骤 ①图像亮度调整、去噪、锐化 首先,将原RGB图像转为灰度图像。 为提高图像对比度,采用分段线性变换的方法,但在此次实验中,提高图像对比度后效果较差或作用不大,故不采取。 由于在对图像求边界时,易遭受鸡蛋外壳表面纹状和背景脉冲噪声的影响,同时为保证图像细节,采用中值滤波的方法,对灰度图像进行去噪,然后利用拉普拉斯滤波进行线性锐化,以中和中值滤波使细节淡化的部分。 ②高频强调、求边界、去除蛋壳边界 在对图像求边界的同时进行高频强调,以增强图像中变化率较大的区分度,即边界或轮廓部分。 由于在对连通区域进行标记时,常受到鸡蛋外壳的干扰,即使标记时取第二大连通域。出于对检测本身的考虑,鸡蛋壳以外的部分无需考虑在其中,故在去除鸡蛋壳的同时,将蛋壳以外的像素全部置零。具体方法是,将去噪、锐化后的原图像转化为二值图像,先执行腐蚀操作,使其缩减一圈,然后与求边界后的图像相点乘。 ③形态学处理 由于求边界后的二值图像轮廓边缘较细,易将原本连通的区域边缘部分分开,从而造成检测中的误差问题。为此,采用形态学操作中的膨胀、闭运算、腐蚀操作使边缘轮廓部分稍微加粗。 ④ Blob法求连通域、按面积标注破损位置 采用八连接标注连通域,并计算连通域的个数,每个连通域的面积、中心点以及外接矩形。然后对面积进行排序,找到最大的破损标注位置。如果连通域大于3个,则只标出3个最大的连通域;若连通域小于三个,则全部用其外接矩形标出。
⑤计算检测精度 首先利用Ostu(最大类间方差法)求使图像二值化的最佳全局阈值,利用此阈值将原灰度图像转为二值图像,得到前景分割的结果M;然后分别设定检测阈值和标注阈值。通过手动标注得到标注的模板G,再由④中的检测结果A计算检测破损率和实际破损率。 在计算bad和badg之前,为保证检测区域是否为真实破损区域,先将标注的检测结果与手动标注的模板进行点乘操作,运算后,留下的轮廓才是真正的破损区域。 计算准确率时,需要设置检测阈值th和标注阈值thg,且按以下标准计算TP(被预测为好蛋的坏蛋)、FP(被预测为好蛋的好蛋)、FN、TN; 最后通过公式求得准确率。 经计算,得出准确率为1。
代码文件
放在一个文件夹即可运行。 1.主函数main.m
%% 1.读取图像并转为灰度值
clc;clear;close all;
input_path = './data/dataset/'; %原图路径
output_path = './data/result/'; %检测结果保存路径
labelmoban_path = './data/label_moban/'; %标注模板路径
outlabel_path = './data/result_label/';%检测结果带标签路径
%% 将标注好的图像二值化为标注模板
label_path = './data/label/'; %原图标签路径
biaozhumoban(label_path,labelmoban_path);
%% 生成检测结果+计算检测精度
egg_pre(input_path,output_path); %预测图像输出在/result/
[Accuracy,bad] = detect_precision(input_path,output_path,labelmoban_path);
disp('准确率:');disp(Accuracy);
2.函数文件biaozhumoban.m
function [] = biaozhumoban(label_path,labelmoban_path)
files_label = dir([label_path '*.jpg']);
l = length(files_label);
for i = 1:l
I_label = imread([label_path files_label(i).name]);
Sign_I = imbinarize(im2gray(I_label),0.95);
imwrite(Sign_I,[labelmoban_path files_label(i).name(1:end-9) 'label_moban.jpg']);
end
3.函数文件egg_pre.m
function [] = egg_pre(input_path,output_path)
files = dir([input_path '*.jpg']);
leng = length(files);
for k = 1:leng
I = rgb2gray(imread([input_path files(k).name]));
%figure,imshow(I);title('原图')
%中值滤波去除脉冲噪声,保持图像细节
mid_I = medfilt2(I,[5,5]); %5*5的中值滤波窗口
% figure,imshow(mid_I);title('平滑滤波')
% 4.锐化滤波
%拉普拉斯滤波线性锐化
mask = [1,1,1;1,-8,1;1,1,1];
L_I = mid_I-imfilter(mid_I,mask,'replicate');
% figure,imshow(L_I);title('拉普拉斯滤波')
%非线性锐化:锐化因邻域平均导致的模糊图像:sobel
% 5.高频强调+求边界
ed = edge(L_I,'canny',0.18);
% ed = edge(L_I,'sobel',0.04);
% figure,imshow(ed);title('高频强调+边界')
%去除蛋壳边界
I2 = imbinarize(L_I);% 转为二值图像
ed1 = bwmorph(I2,'erode',1); %腐蚀
ed = ed .* ed1;
% figure,imshow(ed)
% 6.形态学处理
expe = bwmorph(ed,'dilate',2);
%figure,imshow(expe);title('膨胀');
%闭运算:先膨胀后腐蚀
BW = bwmorph(expe,'close',1);
%figure,imshow(BW);title('闭运算')
corr = bwmorph(BW,'erode',1);
% figure,imshow(corr);title('腐蚀');
BW = corr;
figure,imshow(BW);title('最终结果');
% 7.Blob法
%1.求连通域,标注破损位置
[L,num] = bwlabel(BW,8);
%L : 标记矩阵
%num : 连接分量的总数
%BW : 输入的二值图像
%8 : 指定期望连接方式(4,8)
%2.计算连通域个数,每个连通域面积、中心点、外接矩形
stats = regionprops(L,'basic');
%stats[Area,Centriod,BoungingBox]
%连通域面积、中心点、外接矩形
%3.按照连通域面积大小进行排序
N = size(stats,1);
for i=1:N-1
for j=1:N-i
if stats(j).Area < stats(j+1).Area
tmp = stats(j);
stats(j) = stats(j+1);
stats(j+1) = tmp;
end
end
end
%4.对符合要求的连通域画标记
if num >= 3 %如果连通区域大于等于3个,只取前三个最大值
for i=1:3
boundingbox1 = stats(i).BoundingBox;
x = boundingbox1(1);
y = boundingbox1(2);
width_x = boundingbox1(3);
width_y = boundingbox1(4);
pos = [x y width_x width_y];
rectangle('Position',pos,'EdgeColor','r');
end
else %如果连通区域小于3个,全取
for j = 1:num
boundingbox1 = stats(j).BoundingBox;
x = boundingbox1(1);
y = boundingbox1(2);
width_x = boundingbox1(3);
width_y = boundingbox1(4);
pos = [x y width_x width_y];
rectangle('Position',pos,'EdgeColor','r');
end
end
imwrite(BW,[output_path files(k).name(1:end-4) '_pre.jpg']); %保存
end
end
4.函数文件detect_precision.m
function [accuracy,bad] = detect_precision(input_path,output_path,labelmoban_path)
files = dir([input_path '*.jpg']);
leng = length(files);
% 9.计算检测精度
th = 2; %检测阈值
thg = 5; %标注阈值
%检测准确率
bad = zeros(leng,2);
FN=0;TP=0;TN=0;FP=0;
for j = 1:leng
%a)求鸡蛋mask,大津法阈值+二值化
gray_I = rgb2gray(imread([input_path files(j).name]));
level = graythresh(gray_I); %全局阈值
M = imbinarize(gray_I,level); %前景分割结果
%figure,imshow(M),title('前景分割结果');
M = uint8(M);
I_pre = imread([output_path strcat(files(j).name(1:end-4),'_pre.jpg')]);%I_pre为检测效果
groundtruth = imread([labelmoban_path strcat(files(j).name(1:end-4),'_label_moban.jpg')]);
%groundtruth:标签模板
%将检测结果与实际标注模板相与后,存在部分即为检测真实部分
I_pre = I_pre .* groundtruth;
%figure,imshow(I_pre);title('检测结果.*标注模板')
%1.定义基本变量
bad(j,1) = sum(sum(M.*I_pre))./sum(sum(M)); %检测破损率
bad(j,2) = sum(sum(M.*groundtruth) ./ sum(sum(M))); %实际破损率
if bad(j,2)<thg && bad(j,1)>th
FN = FN+1;
end
if bad(j,2)<thg && bad(j,1)<th
TP = TP+1;
end
if bad(j,2)>thg && bad(j,1)>th
TN = TN+1;
end
if bad(j,2)>thg && bad(j,1)<th
FP = FP+1;
end
end
accuracy = (TP+TN)/(TP+TN+FP+FN);
|