菌体表型提取(一)

1. 写在前面

菌体表型提取是在生物科学实验室拿到的第一个项目,具体要做的就是用电脑代替人工,更快速而精准地测量到菌体样本的一些表型数据,比如菌盖的直径、菌柄的长度等。当然这里还是需要人工对每个样本的特定部位拍照和记录,之后的事情就交给代码啦。由于我自己知识水平有限,以下叙述难免会有错误或是不明智的解决方法,欢迎大家指正!
(注:文章中所用到的MATLAB函数详解可以在官方文档查到。)


2. 提取像素长度

因为我们所需要的数据大部分都是长度,而如何重拍摄的图像中得到我们要的那一段曲线的真实长度喃?那就需要一个比例尺,就像我们会在地图中看到的1cm代表500m这样一个比例尺。对应到我们手中的图像,这个比例尺就变成了1个像素代表多少毫米的长度。
同等拍摄条件下的直尺图片
所以,有了这张图。

我们在拍摄菌体图片的环境下,拍摄一张这样的直尺图,这样我们就可以从这张图中找到所需要的比例尺了。

但是,问题又来了。我怎么用代码告诉电脑这个图中多长的距离是1cm喃?
这个问题当时真的烦到我了,因为这个直尺是透明的,没办法直接用二值化提取联通区域这样的方法让代码能找到这块长度区域(如果这是一段长3cm的红色长方形纸条就能这样操作)。如果这张图不好操作,能不能让它变得好操作一点喃?
加上3cm标注的直尺图片
于是,有了这张图。

可以看到我在直尺的左下角加了两条红色的竖线,来标注我所需要的3cm长度。这样简单处理过了图片就很容易确定标度尺了。

%%
% 3cm基准线标记
[xLength, yLength, dim] = size(length_Label_RGB_Matrix);
for ii=1:xLength
    for jj=1:yLength
        if((length_Label_RGB_Matrix(ii,jj,1)>150&length_Label_RGB_Matrix(ii,jj,2)<100&length_Label_RGB_Matrix(ii,jj,3)<100)==0) 
            length_Label_RGB_Matrix(ii,jj,1)=0;
            length_Label_RGB_Matrix(ii,jj,2)=0;
            length_Label_RGB_Matrix(ii,jj,3)=0;   
        else
            length_Label_RGB_Matrix(ii,jj,3)=256;
        end
    end
end
length_Label_Red_Matrix = length_Label_RGB_Matrix(:,:,3);
%%
% 二值化定位左右x坐标
Threshold                = graythresh(length_Label_Red_Matrix);
length_Label_BW_Matrix   = im2bw(length_Label_Red_Matrix, Threshold);
imLabel = bwlabel(length_Label_BW_Matrix);                
stats   = regionprops(imLabel,'Centroid');    
centroid  = cat(1, stats.Centroid);
%%
% 计算每个像素长度(mm)
figure;
imshow(~length_Label_BW_Matrix);
hold on
for i=1:2 
    plot(centroid(i,1), centroid(i,2), 'b*'); 
end
hold off
length_Per_Pixel = 30.0/(compute_Distance_Of_2D(centroid(1,1), centroid(1,2), centroid(2,1), centroid(2,2)));
%%

通过确定RGB图像中红色的像素点,得到尺图的目标二值化图像。
红色标尺二值化图像
其中用蓝色的星号标注出了两条红色标度线的重心,最后计算两重心直线连线的像素数量,就是3cm长度所对应的像素数量,进而得到单个像素的长度。

由于RGB图像处理速度相对较慢,对于一批样本图像,我们只需要处理标尺图像一次得到单像素的实际长度,再将其值保存用于计算该批次的所有样本图像即可。


3. 二值化处理

菌柄样图
上面是一张菌柄的图片,我拿到的第一批图片的质量大概就是这样的。

对于这张图片,菌柄与背景颜色的区别度还是蛮高的。人为制定一些规则切割出菌柄的大致区域后用最常用的图像二值化方法处理就可以得到菌柄的二值化图像,用于后续处理。
二值化图像
最初得到的图像大概是这个效果。

可以看到图片中还是存在一些噪点的,所以还需要对图片进行去噪。

denoised_Matrix = bwareaopen(origin_Matrix,degree);
%使用bwareaopen函数一步就可以达到效果
%函数的效果是去掉图片中像素数小于degree的所有连通区域

去噪之后还需要将图片中的标签区域和菌柄区域分开。这个切割函数的就比较白痴了,原理大概可以解释如下:
1. 从左到右遍历原BW矩阵的所有列,当有一列的白色像素数小于一个阈值,而这一列的下一列的白色像素数大于等于该阈值,则将该列记作标签区域最左边一列。
2. 从之前找到的标签区域最左边一列继续往右遍历BW矩阵,当有一列的白色像素数大于等于一个阈值,而这一列的下一列的白色像素数小于该阈值,则将该列记作标签区域的最右边一列。
3. 从右往左遍历原BW矩阵,用和1一样的判定方法找到菌柄区域最右边一列。
4. 最后用得到的三个列坐标切合原BW矩阵就可以得到分割开的标签矩阵和菌柄矩阵了。

切割方法代码如下:

function [object_Matrix, label_Matrix] = divide_Label(origin_Matrix)
%DEVIDE_LABEL 将原始图片分割成目标区域和标签区域
%   origin_Matrix 原始图片二值化矩阵
origin_Matrix = double(origin_Matrix);
count_First_Column       = 0;
count_Mind_Column        = 0;
count_Last_Column        = 0;
threshold                = .4;
threshold_Number         = 50;
threshold_Margin_Offset  = 160;
[~,yLength]              = size(origin_Matrix);

%找到图像区域中标签最左边一列
for ii = 1:yLength - 1
    if length(find(origin_Matrix(:,ii)>threshold))<threshold_Number && length(find(origin_Matrix(:,ii+1)>threshold))>=threshold_Number
        count_First_Column = ii - threshold_Margin_Offset;
        if count_First_Column <= 0
            count_First_Column = 1;
        end
        break;
    end
end
%找到图像区域中标签最右边一列
for jj = ii + 30:yLength - 1
    if length(find(origin_Matrix(:,jj)>threshold))>=threshold_Number && length(find(origin_Matrix(:,jj+1)>threshold))<threshold_Number
        count_Mind_Column = jj + threshold_Margin_Offset;
        break;
    end
end
%找到图像区域中样本最右边一列
for jj = yLength - 1:-1:count_Mind_Column + 1
    if length(find(origin_Matrix(:,jj)>threshold))<threshold_Number && length(find(origin_Matrix(:,jj-1)>threshold))>=threshold_Number
        count_Last_Column = jj + threshold_Margin_Offset;
        if count_Last_Column >= yLength
            count_Last_Column = yLength - 1;
        end
        break;
    end
end

label_Matrix  = origin_Matrix(:,count_First_Column:count_Mind_Column);
object_Matrix = origin_Matrix(:,count_Mind_Column:count_Last_Column);

end

这个切割的方法非常白痴…后面我还用这个方法对得到的目标BW矩阵进一步裁剪,目的是使后续处理的目标矩阵更小一点。(可能提高的性能还不如这里切割浪费的…)


4. 第一部分结束

对于原始图像的初始化处理大概就完成了。总结一下,通过第一部分的处理,我们从原始图像RGB矩阵得到了每张图像的目标区域BW矩阵(菌柄、平放菌盖和菌盖切面)和每张图像对应的各自的标签区域BW矩阵。同时成功从标记过的标尺图像提取出每个像素的实际长度,用于后续的长度计算。

当然对以上问题我所使用解决方法只是我从解决实际问题角度出发想到的最简单的方法,写出来的代码虽然效率不高,但也成功地完成了它的任务。如果有读者能对我所使用的粗鄙的方法加以改进或是提供更先进的方法,我会非常感激!

猜你喜欢

转载自blog.csdn.net/conlink/article/details/79938448