[关闭]
@zsh-o 2018-03-17T15:25:19.000000Z 字数 3887 阅读 1861

找出图像中的异常白斑

图像处理


这是导师给的一个任务,大致意思是找出图像中的异常的白斑,像这样
example1

实验一

第一感觉是点既然基本上都是纯白色的,那只需要根据阈值把图片转换为二值的,再求一个闭操作,最后统计面积删掉面积过小和过大的区域

首先用imhist(img)做了一次像素统计
imghist
统计图很喜人,那直接把阈值设为完事了呀,设为也行呀

  1. figure;
  2. subplot(1,3,1);
  3. imshow(img);
  4. bw_img = imbinarize(img, 0.85);
  5. subplot(1,3,2);
  6. imshow(bw_img);
  7. se = strel('disk', 20);
  8. closeBW = imclose(bw_img, se);
  9. subplot(1,3,3);
  10. imshow(closeBW);

二值化效果

最后删除面积过小的区域

  1. s = regionprops(closeBW, 'area', 'boundingbox');
  2. areas = [s.Area];
  3. rects = cat(1, s.BoundingBox);
  4. rects_1 = rects(areas>50,:);
  5. figure;
  6. subplot(1,2,1);
  7. imshow(closeBW);
  8. for i = 1:size(rects_1, 1)
  9. rectangle('position', rects_1(i, :), 'EdgeColor', 'r');
  10. end
  11. subplot(1,2,2);
  12. imshow(img_orginal);
  13. for i = 1:size(rects_1, 1)
  14. rectangle('position', rects_1(i, :), 'EdgeColor', 'r');
  15. end

结果

这结果也太差强人意了呀,第一反应是阈值设的过大了,现在改为
结果
恩,这次怎么说呢,跟预期有点差别呀~~~
中间的很多点异常的白斑都没有识别出来,这些白斑的像素值比阈值要小,但右边的地板部分却被标记出来了,这说明右边的地板的灰度值也要比需要需要标记的白斑的灰度值要大

实验二

这种基于阈值的方法基于的统计的,而上面实验一的方法是基于全局的像素统计的,也就是那个统计图,我们的先验是这些异常的白斑是“白斑”(啊哈),他们的灰度值普遍比较高。另一个隐藏的先验是这些白斑其实是与周围像素差别比较大的(对比度高的),由此人眼才能轻易识别出来,像
对比度
这样我们只需要找到相近像素中的小的异常区域就可以了

先根据相近像素把图片中的大区域检测出来,这样,每一个大区域内的图像都是相近的,而需要检测的目标点,是在这些大区域中相对灰度值高的小区域,这样小区域的选取就不必依赖一个全局的阈值,因为有时候一个目标小区域的灰度值可能要小于另一个大区域的平均灰度值,从而被这个全局的阈值过滤掉了

首先我们在上面的直方图中观察到像素的大多数都分布在,现在要把该分布拉长到更广的域,这里,使用了一个函数,为偏移

  1. function [out] = sigmoid(img, b)
  2. out = 1./(1+exp(-(img-0.5-b).*10));
  3. end
  1. clear;clc;
  2. img_rgb = imread('../2.jpg');
  3. img_gray = double(rgb2gray(img_rgb))./255;
  4. figure;
  5. subplot(1,2,1);
  6. imshow(img_gray);
  7. img_rescale = sigmoid(img_gray, 0);
  8. subplot(1,2,2);
  9. imshow(img_rescale);
  10. figure;
  11. subplot(1,2,1);
  12. imhist(img_gray);
  13. subplot(1,2,2);
  14. imhist(img_rescale);

像素分布拉伸

这个分布中可以看出来,有两个峰值,很像是两个高斯函数的混合(其实更像是三个高斯函数,在0.8的位置也有一个高斯函数,但是方差很小),接下来要做的就是把大区域区分出来,因为这只是一次实验和尝试,所以采取的是最简单的做法,也可以用一些更复杂的区域方法来做,像相似区域生长等,这里只是把两个分布分割出来

把图像分割成两个大区域一个是大于0.5的一个是小于0.5的

  1. hist_min = 0.5;
  2. img_1 = img_rescale;
  3. img_1(img_rescale<hist_min) = 0;
  4. img_2 = img_rescale;
  5. img_2(img_rescale>=hist_min) = 1;
  6. figure;
  7. subplot(1,2,1);
  8. imshow(img_1);
  9. subplot(1,2,2);
  10. imshow(img_2);

区域分割

二值化处理选择区域,这里以简单起见,只考虑了低灰度部分,以低灰度进行区域选择,高灰度那部分区域就是低灰度取余

  1. img_1_bw = imbinarize(img_1, hist_min-0.1);
  2. img_2_bw = ~img_1_bw;
  3. figure;
  4. subplot(1,2,1);
  5. imshow(img_1_bw);
  6. subplot(1,2,2);
  7. imshow(img_2_bw);

image_1c8q6l6gk1sll614uhu10a8nkh4g.png-70.9kB
然后进行了一个闭操作进行区域柔化和合并

  1. se = strel('disk', 30);
  2. close_1 = imclose(img_1_bw, se);
  3. close_2 = ~close_1;
  4. figure;
  5. subplot(1,2,1);
  6. imshow(close_1);
  7. subplot(1,2,2);
  8. imshow(close_2);

image_1c8q6q8mr19mqrnqvu01ibl1tuo4t.png-52.7kB

再对低灰度区域求一次闭操作,合并中间的一些有可能是白斑的小区域点和一些噪音点,并统计区域面积,删除面积太小的区域

  1. % 可变
  2. se = strel('disk', 30);
  3. close_2_2 = imclose(close_2, se);
  4. figure;
  5. imshow(close_2_2);
  6. [L, num] = bwlabel(close_2_2,4);
  7. R = regionprops(close_2_2, 'area');
  8. areas = cat(1, R.Area);
  9. % 删掉小于500的区域
  10. areas_lab = find(areas<500);
  11. L(ismember(L, areas_lab)) = 0;
  12. L = logical(L);
  13. figure;
  14. imshow(L);

image_1c8q790rk910lu21iqbemcgsm5a.png-6.6kB

用得到的两组大区域把原图对应区域抠出来,并画出灰度直方图

  1. img_combined_1 = img_rescale;
  2. img_combined_1(~L) = 0;
  3. img_combined_2 = img_rescale;
  4. img_combined_2(L) = 0;
  5. figure;
  6. subplot(1,2,1);
  7. imshow(img_combined_1);
  8. subplot(1,2,2);
  9. imshow(img_combined_2);
  10. figure;
  11. subplot(1,2,1);
  12. imhist(img_combined_1);
  13. subplot(1,2,2);
  14. imhist(img_combined_2);

image_1c8q7ikgttte24jgnchu12pm64.png-188.9kB

从上图可以看出来,大区域的像素按照像素相似性被分离出来了,然后要做的是在各自的大区域中寻找异常小区域。根据各自的直方图进行二值化处理,根据阈值得到目标白斑区域,再把这两个组区域合并

  1. img_combined_bw_1 = imbinarize(img_combined_1, 0.7);
  2. img_combined_bw_2 = imbinarize(img_combined_2, 0.9);
  3. figure;
  4. subplot(1,2,1);
  5. imshow(img_combined_bw_1);
  6. subplot(1,2,2);
  7. imshow(img_combined_bw_2);
  8. img_combined_bw = img_combined_bw_1|img_combined_bw_2;
  9. figure;
  10. imshow(img_combined_bw);

image_1c8q7sof31aki0jat01m811lms9.png-17.2kB

最后,又对该组合区域执行一次闭操作,然后统计面积,删除太小的面积,最后留下的区域就是认为的目标区域,再把相应区域在原图上标记出来

  1. se = strel('disk', 20);
  2. img_combined_bw_closed = imclose(img_combined_bw, se);
  3. s = regionprops(img_combined_bw_closed, 'area', 'boundingbox');
  4. areas = [s.Area];
  5. rects = cat(1, s.BoundingBox);
  6. %执行一次闭操作,然后删除小面积区域
  7. rects_1 = rects(areas>40,:);
  8. figure;
  9. subplot(1,2,1);
  10. imshow(img_combined_bw_closed);
  11. for i = 1:size(rects_1, 1)
  12. rectangle('position', rects_1(i, :), 'EdgeColor', 'r');
  13. end
  14. subplot(1,2,2);
  15. imshow(img_gray);
  16. for i = 1:size(rects_1, 1)
  17. rectangle('position', rects_1(i, :), 'EdgeColor', 'r');
  18. end

image_1c8q882giqlcob91t741sphrmq16.png-154.7kB

最后可以看出来,效果要比上面的实验一好很多,但仍然有很多可以改进的地方,就像前面说的大区域的选取方式,如果按照上面阈值的方法的话,高斯分布个数的选取,也就是说上面的是假定2个大区域好还是3个好,虽然这里选择的是两个,一些区域生长的方法可以回避这个问题,而且效果应该会更好。剩下的就是两次阈值的确定,一次是分割大区域时,另一次是在每一个大区域中选择目标白斑时,恩,应该有更“智能”的方法 :)

这两次实验的方法本质是考虑了全局和局部的关系

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注