本文主要使用了matlab中的imfindcircles函数,功能参数详见其文档。其主要使用Hough变换的方法提取图片中圆形信息。
- 使用此函数需要注意的几点:
1)当 radius(或 rmin)的值小于或等于 5 时,imfindcircles 的准确度会受到限制;
2)imfindcircles 找不到圆心位于图像区域之外的圆形;
3)imfindcircles 会预处理二值(逻辑值)图像以提高结果的准确度。在处理真彩色图像之前,它使用 rgb2gray 函数将其转换为灰度图像。
%此程序用来识别和跟踪视频中的颗粒及其轨迹
%% 读取图片信息
% %test;
% RGB= imread('F:\desktop\轨迹熵\实验\目标检测跟踪\granule4.png');
% imshow(RGB)
% 将视频拆解成图片;
video = VideoReader('E:\20220326\125-2fps.avi');
N_Frames = video.NumFrames; %得到帧数
H = video.Height; %得到高度
W = video.Width; %得到宽度
Rate = video.FrameRate;
% Preallocate movie structure.
% mov(1:nFrames) = struct('cdata',zeros(H,W,3,'uint8'),'colormap',[]);
%read one frame every time
% for i = 1:1000
% mov(i).cdata = read(video,i);
% RGB= mov(i).cdata;
% % disp('当前播帧数:'),disp(i);
% % imshow(RGB);
% end
% 由于不需要存储每张图片,所以不需要mov结构来储存数据;
%待将此处化为循环结构;
%test
i=1156810;
RGB=read(video,i);
bw=imbinarize(RGB,'global'); %imfindcircles函数本身是先把图片转为二值图,但是并不会选择性的去掉连通像素小于某一阈值的区域;
%所以此步骤看似重复,但其实是为bwareaopen函数做准备;
%由于图片本来就是灰度图,直接转为二值图;
bw=bwareaopen(bw,60); %去掉连通像素小于60的区域;
%注意在识别之前,需要先取一个很大的半径范围,进而确认目标是在哪个范围之内,缩小范围,进而节省上述手动划线过程;
%如果被识别的颗粒直径有变化需要修改此参数!!!!!!!!!!!!!!!
Rmin = 7;
Rmax = 16;
[center, radius]=imfindcircles(bw,[Rmin Rmax],'Sensitivity',0.9,'ObjectPolarity','bright'); %调节ObjectPolarity属性可以实现对不同颜色的区分;
imshow(bw);
h=viscircles(center,radius); %显示圆形轮廓;
% immatrix=imshow(bw); %将图片化为数值矩阵;
%数据输出的文件夹目标名;
folder_name='E:\20220326';
% %服务器上使用工作文件当前文件夹;
%待查
% folder_name=pwd; %显示当前文件夹绝对路径字符串,当然也可以不用绝对路径;
%在当前文件夹(程序所在文件夹)创建data数据文件;
%如果文件夹已经存在,则会发出警告,但是无碍下一步操作;
% mkdir('data'); %服务器上可使用相对目录;
mkdir(strcat(folder_name,'\data' ));
%注意在服务器下可以使用相对路径,即不使用绝对路径;
fid_position=fopen(strcat(folder_name,'\data\125-2fps_position.txt'),'w');
fid_problem=fopen(strcat(folder_name,'\data\125-2fps_position_problem.txt'),'w');
tic
for i=376841:1:739966
RGB=read(video,i);
bw=imbinarize(RGB,'global'); %由于图片本来就是灰度图,直接转为二值图;
%此处为了提高后续的识别成功率;
%多出此步骤,主要是为了后面消除连片像素小的区域,imfindcircles函数本身当然也会有二值化过程,速度
%总体与之前基本持平;
%默认情况下,imbinarize 使用 Otsu
%方法,该方法选择特定阈值来最小化阈值化的黑白像素的类内方差;
bw=bwareaopen(bw,60); %去掉连通像素小于60的区域;
%注意在识别之前,需要先取一个很大的半径范围,进而确认目标是在哪个范围之内,缩小范围,进而节省上述手动划线过程;
%如果被识别的颗粒直径有变化需要修改此参数!!!!!!!!!!!!!!!
Rmin = 7;
Rmax = 16;
[center, radius]=imfindcircles(RGB,[Rmin Rmax],'Sensitivity',0.9,'ObjectPolarity','bright'); %调节ObjectPolarity属性可以实现对不同颜色的区分;
%此处sensitivity的值在0.9附近对结果影响很敏感,0.9为一经验值;
% disp(i);
if length(center)==10
for j=1:1:10
fprintf(fid_position,'%15.10f %15.10f\n',center(j,:));
end
elseif length(center)>10
disp('oh no');
% imshow(bw);
% h=viscircles(center,radius); %显示圆形轮廓;
% drawnow;
disp(i);
disp(center);
%有时识别出的圆的数量大于实际数量,需要剔除多于的圆心数据;
D=pdist(center); %计算行与行之间的欧几里得距离;
Z=squareform(D); %为了方便识别行号和列号,将上述结果转为方阵,第i行j列,即代表center第i行与第j行所代表向量之间的距离;
U=triu(Z); %为了方便识别行号和列号,只取出矩阵的上三角部分;
[row,col]=find(U>0 & U<10); %如果圆心距离大于0小于10,则认为两个圆心重复;如果被识别的颗粒直径有变化需要修改此参数!!!!!!!!
center(row,:)=[]; %任意去掉其中重复的圆心;
if length(center)~=10
disp(['There is a problem with this frame:',num2str(i)]); %以防万一;
disp(center);
fprintf(fid_problem,'%10d\n',i);
for j=1:1:10
fprintf(fid_position,'%15.10f %15.10f\n',zeros(1,2)); %为了后续处理数据的方便(数据长度不发生变化),在有问题的插入0值,供后续更改;
%注意如果被检测圆的数量发生改变,这里需要更改数目!!!!!!
end
else
disp('successful!!!');
disp(center);
for j=1:1:10
fprintf(fid_position,'%15.10f %15.10f\n',center(j,:));
end
end
%有时识别出的圆的数量小于实际数量,暂无更好的办法,此处调节识别圆的sensitivity,但是有风险;
else
disp(['There is a problem with this frame:',num2str(i)]); %以防万一;
disp(center);
fprintf(fid_problem,'%10d\n',i);
for j=1:1:10
fprintf(fid_position,'%15.10f %15.10f\n',zeros(1,2)); %为了后续处理数据的方便(数据长度不发生变化),在有问题的插入0值,供后续更改;
%注意如果被检测圆的数量发生改变,这里需要更改数目!!!!!!
end
end
% imshow(bw)
% h=viscircles(center,radius); %显示圆形轮廓;
% drawnow; %为了连续显示图窗;
end
fclose(fid_position);
fclose(fid_problem);
toc
%注意检查各矩阵是否与实际颗粒的数目是否一样;
%% 将图片转为二值图片,凸显边缘
% % I = rgb2gray(RGB); %图片本来就是灰度图片,不需要再转为灰度图;
% imhist(RGB); %查看图片的灰度分布
% bw = imbinarize(RGB); %由于图片本来就是灰度图,直接转为二值图;
% % imshow(bw)
% imhist(bw); %查看图片的灰度分布
% imwrite(bw,'E:\20220326\125-2fps.png');
%
% % 删除包含少于阈值像素的所有对象
% bw = bwareaopen(bw,60);
% imshow(bw)
% % 填充笔帽中的间隙
% se = strel('disk',2);
% bw = imclose(bw,se);
% imshow(bw)
%
% %填充任何孔洞,以便可以使用 regionprops 来估计每个边界所包围的面积
% bw = imfill(bw,'holes');
% imshow(bw)
% %手动估算颗粒直径的长度
% d = drawline; %在figure图中对颗粒的直径划线
% pos = d.Position;
% diffPos = diff(pos);
% diameter = hypot(diffPos(1),diffPos(2))
- 注意此识别程序并不能完全理想的挑选出所有的圆形,程序中把异常值的情况挑选了出来,输出到了
‘E:\20220326\125-2fps_position_problem.txt’
- 识别正常的情况:
- 识别异常情况:
参考文献: