PET/CT图像的纹理特征提取

Author: Zongwei Zhou 周纵苇
Weibo: @MrGiovanni
Email: zongweiz@asu.edu


Please cite this paper if you found it useful. Thanks!
Wang H, Zhou Z, Li Y, et al. Comparison of machine learning methods for classifying mediastinal lymph node metastasis of non-small cell lung cancer from 18F-FDG PET/CT images[J]. 2017, 7.

<img src="http://upload-images.jianshu.io/upload_images/1689929-519d3b5f49a4c31a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="0.001" height="0.001"/>


目的

检验纹理特征对3d-PET/CT图像分类的效果。

简介

在使用传统分类器的时候,和深度学习不一样,我们需要人为地定义图像特征,其实CNN的卷积过程就是一个个的滤波器的作用,目的也是为了提取特征,而这种特征可视化之后往往就是纹理、边缘特征了。因此,在人为定义特征的时候,我们也会去定义一些纹理特征。在这次实验中,我们用数学的方法定义图像的纹理特征,分别计算出来后就可以放入四个经典的传统分类器(随机森林,支持向量机,AdaBoost,BP-人工神经网络)中分类啦。

工具

我使用的工具是MATLAB 2014b,建议版本高一点好,因为里面会更新很多的函数库。实验过程尽量简化,本实验的重点是检验纹理特征对PET/CT图像分类的效果,因此,有些常规的代码我们就用标准的函数库足够啦。

参考文档

PORTS 3D Image Texture Metric Calculation Package


1. 直方图-histogram

直方图描述的是一幅图像中各个像素的分布情况,也就是一个对像素做的统计图。
对于一幅灰度图像 I,它每个像素值的范围是0-255,我们对这些像素点做一个统计,遍历整幅图像,统计像素值0,1,2,3,...,255分别出现的次数。统计完以后相当于我们有了256个频数(次数),再把它们转化成频率,也就是每个频数除以总频数:

p(i) = P(i) / ∑P

以像素值作为横坐标,对应的频率作为纵坐标,就可以得到这个灰度图像 I 的直方图啦。

1.1 举栗子:CT图像的直方图

左图是原始的CT图像,右图是该图像的直方图

1. CT图像的像素值范围是-1000~1000。相当于我们需要统计2000个像素值的频数,这样划分的粒度有点太细了,因此

2. 将这-1000~1000的区间20等分,每个像素值投射到20个值。直接导致的结果是图像看上去不那么丰富了,但是这样有利于计算。

3. 分别统计这20个像素值出现的频数,除以总频数转化成频率。这样频率介于[0,1],并且加和为1.

4. 以20个像素值为横坐标,对应的频率为纵坐标,即可画出这个CT图像的直方图。

The end of this 栗子.

1.2 直方图的代码实现

%%%%%
%%%%% Histogram-based computations:
%%%%%

% Compute the histogram of the ROI and probability of each voxel value:
vox_val_hist = zeros(num_img_values,1);
for this_vox_value = 1:num_img_values
    vox_val_hist(this_vox_value) = length(find((img_vol_subvol == this_vox_value) & (mask_vol_subvol == 1) ));
end

% Compute the relative probabilities from the histogram:
vox_val_probs = vox_val_hist / num_ROI_voxels;


% Compute the histogram_based metrics:
texture_metrics(1:6) = compute_histogram_metrics(vox_val_probs,num_img_values);

1.3 基于直方图的PET/CT纹理特征

包括六个值,分别是:

(1) Mean

(2) Variance

(3) Skewness – set to 0 when σ=0

(4) Kurtosis – set to 0 when σ=0 (NOTE: “Kurtosis” and “Excess Kurtosis” differ in that Excess Kurtosis = Kurtosis – 3).

(5) Energy

(6) Entropy (NOTE: We will differentiate between the various entropy calculations in this document, specifying the distribution from which the entropy is computed)

1.4 纹理特征计算实现

%%% Overhead:

% The numerical values of each histogram bin:
vox_val_indices = (1:num_img_values)';

% The indices of non-empty histogram bins:
hist_nz_bin_indices = find(vox_val_probs);


%%% (1) Mean 
metrics_vect(1) = sum(vox_val_indices .* vox_val_probs);

%%% (2) Variance
metrics_vect(2) = sum( ((vox_val_indices - metrics_vect(1)).^2) .* vox_val_probs );

%%%%% IF standard variance is zero, so are skewness and kurtosis:
if metrics_vect(2) > 0
    
    %%% (3) Skewness
    metrics_vect(3) = sum( ((vox_val_indices - metrics_vect(1)).^3) .* vox_val_probs ) / (metrics_vect(2)^(3/2));

    %%% (4) Kurtosis
    metrics_vect(4) = sum( ((vox_val_indices - metrics_vect(1)).^4) .* vox_val_probs ) / (metrics_vect(2)^2);
    metrics_vect(4) = metrics_vect(4) - 3;
    
else
    
    %%% (3) Skewness
    metrics_vect(3) = 0;
    
    %%% (4) Kurtosis
    metrics_vect(4) = 0;
    
end

%%% (5) Energy
metrics_vect(5) = sum( vox_val_probs .^2 );
%%% (6) Entropy (NOTE: 0*log(0) = 0 for entropy calculations)
metrics_vect(6) = -sum( vox_val_probs(hist_nz_bin_indices) .* log(vox_val_probs(hist_nz_bin_indices)) );

注:vox_val_probs表示直方图中的概率值向量,num_img_values表示像素值划分了几等分,相当于上面的栗子中的20.


2. 灰度共生矩阵-GLCM/GTSDM

了解了直方图,我们接下来看看灰度共生矩阵Grey-level co-occurrence matrix GLCM (also called grey tone spatial dependence matrix GTSDM)是个啥。说白了如果直方图是简单的像素概率统计,得到的统计结果是个一维的向量;GLCM就是两个像素之间的共现(共同出现)概率统计,得到的统计结果是个二维的向量。

闹,没看懂。

比如,一幅图中,A处出现了像素值为x的值,如果在距离A处一个特定的地方出现了像素值为y的值,那么得到的GLCM中,坐标(x,y)处的计数加一。假设我们是一个灰度图,x和y的范围都是固定的(0-255),那么也就是说这个统计矩阵也是固定的,是256×256的大小,矩阵中的数值就是频数统计结果,最后转换成频率就是GLCM啦。

也就是说GLCM刻画的是一组像素对儿在图像中的分布情况。

2.1 不知道有没有讲清楚,举个栗子。

左图是原始的CT图像,右图是该图像的灰度共生矩阵

1. CT图像的像素值范围是-1000~1000。相当于我们需要统计2000个像素值的频数,这样划分的粒度有点太细了,因此

2. 将这-1000~1000的区间20等分,每个像素值投射到20个值。直接导致的结果是图像看上去不那么丰富了,但是这样有利于计算。

以上两步和直方图一样。

3. 锁定CT图中一个点A,坐标(i,j)。A点的像素值是x,在CT图中,距离A点向右del_i个像素,向下del_j个像素的位置B点,坐标(i+del_i, j+del_j),B点的像素值是y,那么,GLCM矩阵中的位置(x,y)计数加一。注意哦,这里的x,y是原来的CT图像的像素值大小,i,j,del_i,del_j,x,y的意义可不要搞混喽!

4. 遍历CT图中所有的点,方法就是按照第三步这么统计。注意:del_i和del_j这两个偏移量是预先设定好的,也就是说可以认为是常量。

5. 分别将统计完的矩阵中的频数,除以总频数转化成频率。这样频率介于[0,1],并且加和为1.

6. 以20个像素值为横坐标,20个像素值为纵坐标,中间的值表示对应的频率,就得到了这个CT图像的GLCM可视化图。

如此这般,得到的GLCM矩阵描述的就是一组像素对儿在原始CT图像中,在固定偏移(del_x,del_y)中的共现概率分布。

The end of this 栗子.

2.2 简易的2D-image-GLCM代码实现

GLCM2 = graycomatrix(CTimage, 'Offset',[4,4], 'NumLevels',20,'GrayLimits',[]);

2.3 2D-image向3D-image拓展

对于一幅3D的图像,它的GLCM矩阵计算方法与2D图像类似,得到的GLCM矩阵依旧是一个二维的哦,因为GLCM的横纵坐标是像素值,和原始图像的维度无关,即使是个4D图像,它的GLCM矩阵也同样是二维的。

与二维图像相比,三维图像在计算GLCM的步骤类似,只有栗子2的第三步需要做一个改动:

3. 锁定3D-CT图中一个点A,坐标(i,j,k)。A点的像素值是x,在CT图中,距离A点向右del_i个像素,向下del_j个像素,向外del_k个像素的位置B点,坐标(i+del_i, j+del_j, k+del_k),B点的像素值是y,那么,GLCM矩阵中的位置(x,y)计数加一。注意哦,这里的x,y是原来的CT图像的像素值大小,i,j,k,del_i,del_j,del_k,x,y的意义可不要搞混喽!

厉害的你可能已经发现,对于一个固定的偏移量del,可以取0或者±del,一共是三个取值,那么对于2D图像,就有3×3-1种情况,如下图所示:

对于3D图像,就有3×3×3-1种情况。

2.4 基于GLCM的PET/CT纹理特征

一共有19个,分别是:

% The first 14 entries in the output are from [Haralick, 1973]:
(1) Angular second moment (called "Energy" in Soh 1999)

(2) Contrast

(3) Correlation

(4) Sum of squares variance


(5) Inverse Difference moment (called "Homogeneity" in [Soh, 1999])

(6) Sum average

(7) Sum variance

(8) Sum Entropy

(9) Entropy

(10) Difference Variance

(11) Difference Entropy

(12) Information Correlation 1

(13) Information Correlation 2

*(14) Maximal Correlation Coefficient (不做计算,永远是0) *

% The next five entries in the output are from [Soh, 1999]:
(15) Autocorrelation

(16) Dissimilarity

(17) Cluster Shade

(18) Cluster Prominence

(19) Maximum Probability

% The next entries are from [Clausi, 2002]:
(20) Inverse Difference

中间量的计算



2.5 纹理特征计算实现

% (1) Angular second moment
metrics_vect(1) = sum( p(:).^2 );

% (2) Contrast (for some reason, the paper does not explicitly state p_xmy
% here):
metrics_vect(2) = sum( ((0:(N_g-1))' .^2) .*  p_xmy  );

% (3) Correlation (there is mathematical ambiguity in the nature of the sum as
% stated in the paper ; this version has the means subtracted after the sum is 
% taken, which is the proper method for computation):
mu_x = sum( (1:N_g)' .* p_x );
mu_y = sum( (1:N_g)' .* p_y );
sg_x = sqrt( sum( ( ((1:N_g)' - mu_x).^2 ) .* p_x ) );
sg_y = sqrt( sum( ( ((1:N_g)' - mu_y).^2 ) .* p_y ) );

if (sg_x*sg_y) == 0
    metrics_vect(3) = Inf;
else
    metrics_vect(3) = ( sum(ndr(:) .* ndc(:) .* p(:) ) - (mu_x*mu_y)  ) ./ (sg_x*sg_y);
end

% (4) Sum of squares variance (NOTE: \mu is not defined in the paper, we will
% take it to describe the mean of the normalized GTSDM):
metrics_vect(4) = sum( (( ndr(:) - mean(p(:)) ) .^2) .* p(:) );

% (5) Inverse Difference moment
metrics_vect(5) = sum( ( 1 ./ (1 + ((ndr(:)-ndc(:)).^2) )  ) .* p(:) );

% (6) Sum average
metrics_vect(6) = sum( (1:(2*N_g))' .* p_xpy(:) ); % NOTE: p_xpy(1) = 0 , so adds nothing.

% (7) Sum variance
metrics_vect(7) = sum( (((1:(2*N_g))' - metrics_vect(6)) .^2) .* p_xpy(:));

% (8) Sum Entropy (computed above)
metrics_vect(8) = SE;

% (9) Entropy (computed above)
metrics_vect(9) = HXY;

% (10) Difference Variance
mu_xmy = sum( (0:(N_g-1))' .*  p_xmy );
metrics_vect(10) = sum( (((0:(N_g-1))' - mu_xmy) .^2) .*  p_xmy  );

% (11) Difference Entropy
metrics_vect(11) = -sum( p_xmy(p_xmy>0) .* log(p_xmy(p_xmy>0)) );

% (12) and (13) Information Correlations
if (max(HX,HY)== 0)
    metrics_vect(12) = Inf;
else
    metrics_vect(12) = (HXY - HXY1) / max(HX,HY);
end

metrics_vect(13) = sqrt(1-exp(-2*(HXY2-HXY)) );

% (14) Maximal Correlation Coefficient
%%% I don't think we use it, so I'll only code it up if needed.

%%%%%
%%%%% The following are from Soh (1999)
%%%%%

% (15) Autocorrelation
metrics_vect(15) = sum( (ndr(:) .* ndc(:)) .* p(:) );

% (16) Dissimilarity
metrics_vect(16) = sum( abs(ndr(:) - ndc(:)) .* p(:) );

% (17) Cluster Shade
metrics_vect(17) = sum( (ndr(:) + ndc(:) - mu_x - mu_y) .^3 .* p(:) );

% (18) Cluster Prominence
metrics_vect(18) = sum( (ndr(:) + ndc(:) - mu_x - mu_y) .^4 .* p(:) );

% (19) Maximum Probability
metrics_vect(19) = max( p(:) );

%%%%%
%%%%% The following are from Clausi (2002)
%%%%%

% (20) Inverse Difference:
metrics_vect(20) = sum( ( 1 ./ (1 + abs( ndr(:)-ndc(:) ) )  ) .* p(:) );

3. Neighborhood grey tone difference matrix (NGTDM)

NGTDM刻画的是一个像素与其周围像素值的关系。

3.1 举个2D图像的栗子

1. CT图像的像素值范围是-1000~1000。相当于我们需要统计2000个像素值的频数,这样划分的粒度有点太细了,因此

2. 将这-1000~1000的区间20等分,每个像素值投射到20个值。直接导致的结果是图像看上去不那么丰富了,但是这样有利于计算。

以上两步和前面栗子的一样。

3. 锁定CT图中一个点A,坐标(i,j)。对于一个二维图像来说,A点周围应该有8个点,左边分别是(i±1,j±1),(i,j±1),(i±1,j),这8个点的像素范围是1~20(因为步骤2)。求这8个点的像素值的平均值,为A'。那么,设A点的像素值为p_A
NGTDM(p_A) = NGTDM(p_A) + abs(p_A-A');
occur(p_A) = occur(p_A) + 1;

4. 遍历CT图中所有的点,方法就是按照第三步这么统计。我们可以得到两个矩阵NGTDM和occur,它们都是20×1的矩阵,NGTDM记录每个像素值周围的情况,occur记录的是每个像素值在整个CT图像中出现的频数。

5. 分别将统计完的occur中的频数,除以总频数转化成频率。这样频率介于[0,1],并且加和为1。

6. 以20个像素值为横坐标,以它们所对应的NGTDM和occur值为纵坐标,做一个柱状图,就可以得到NGTDM和occur的可视化图。

3.2 3D-NGTDM代码实现

function [NGTDM,vox_occurances_NGD26] = compute_3D_NGTDM(ROI_vol,img_vol,binary_dir_connectivity,num_img_values)

% Placeholder for the NGTDM and number of occurances with full NGDs: 
NGTDM = zeros(num_img_values,1);
vox_occurances_NGD26 = zeros(num_img_values,1);

% Record the indices of the voxels used in the ROI:
ROI_voxel_indices = find(ROI_vol);

% Loop over each voxel in the ROI sub-volume:
for this_ROI_voxel = 1:length(ROI_voxel_indices)
    
    % The index of this voxel in the sub-volume:
    this_voxel_index = ROI_voxel_indices(this_ROI_voxel);
    
    % This voxel must have 26 neighbors (plus itself) to be considered:
    if sum(binary_dir_connectivity{this_ROI_voxel}(:)) == 27
        
        % Determine the [r,c,s] of this voxel:
        [r,c,s] = ind2sub(size(ROI_vol),this_voxel_index);
        
        % Compute the mean value around this voxel:
        this_vox_val = img_vol(this_voxel_index);
        vox_ngd = img_vol((r-1):(r+1) , (c-1):(c+1) , (s-1):(s+1));
        vox_ngd_sum = sum(vox_ngd(:)) - this_vox_val;
        vox_ngd_mean = vox_ngd_sum / 26;
        
        % Add this value to the matrix:
        NGTDM(this_vox_val) = NGTDM(this_vox_val) + abs(this_vox_val-vox_ngd_mean);        
        
        % Increment the number of occurances of this voxel:
        vox_occurances_NGD26(this_vox_val) = vox_occurances_NGD26(this_vox_val) + 1;        
    
    end % Test for full neighborhood    
    
end % Loop over ROI voxels

3.3 基于NGTDM的PET/CT纹理特征

(1) Coarseness

(2) Contrast

(3) Busyness

(4) Complexity

(5) Texture Strength

3.4 纹理特征计算实现

%%% (1) Coarseness
metrics_vect(1) = sum( vox_val_probs .* NGTDM );

% It's the reciprocal, so test for zero denominator:
if metrics_vect(1) == 0
    metrics_vect(1) = Inf;
else
    metrics_vect(1) = 1/metrics_vect(1);
end

%%% (2) Contrast
if N_g > 1 % There is some voxel color differences, so perform calculations as normal:
    % The first term in equation (4):
    first_term_mat = (vox_val_probs * vox_val_probs') .* ( (nd_r-nd_c).^2 );
    first_term_val = sum(first_term_mat(:)) / (N_g * (N_g-1) );

    % The second term in equation (4). Note that the 3D computation
    % necessitates normalization by the number of voxels instead of the n^2 that appears in
    % equation (4). 
    second_term_val = sum(NGTDM) / sum(vox_occurances_NGD26);

    % Record the value:
    metrics_vect(2) = first_term_val * second_term_val;
    
else % There is only a single color, so no contrast to compute, so set to negative:
        metrics_vect(2) = -1;
end 

%%% (3) Busyness
% NOTE: The denominator equals zero in the paperAmadasun 1989. Absolute value inside the
% double-sum is given here, in accordance with 
%
% Texture Analysis Methods ? A Review
% Andrzej Materka and Michal Strzelecki (1998)
%
first_term = sum(vox_val_probs .* NGTDM);

second_term_mat = (nd_nz_r .* nd_nz_p_r) - (nd_nz_c .* nd_nz_p_c);
second_term = sum(abs(second_term_mat(:)));

if second_term == 0
    metrics_vect(3) = Inf;
else 
    metrics_vect(3) = first_term / second_term;
end

%%% (4) Complexity
first_term_num = abs(nd_nz_r - nd_nz_c);
first_term_den = nd_nz_p_r + nd_nz_p_c;

second_term = (nd_nz_p_r .* nd_nz_NGTDMop_r) + (nd_nz_p_c .* nd_nz_NGTDMop_c);

if second_term == 0
    metrics_vect(4) = Inf;
else
    tmp = first_term_num(:) .* second_term(:) ;
    tmp = sum(tmp ./ first_term_den(:)) ;
    metrics_vect(4) = tmp / sum(vox_occurances_NGD26) ;
end

%%% (5) Texture Strength
first_term_mat = (nd_nz_p_r+nd_nz_p_c) .* ( (nd_nz_r - nd_nz_c) .^2 );
first_term = sum(first_term_mat(:));
second_term = sum(NGTDM);

if second_term == 0
    metrics_vect(5) = Inf;
else
    metrics_vect(5) = first_term / second_term ;
end

4. Grey Level Zone Size Matrix (GLZSM)

4.1 基于GLZSM的PET/CT纹理特征

(1) Small Zone Size Emphasis

(2) Large Zone Size Emphasis

(3) Low Gray-Level Zone Emphasis

(4) High Gray-Level Zone Emphasis

(5) Small Zone / Low Gray Emphasis

(6) Small Zone / High Gray Emphasis

(7) Large Zone / Low Gray Emphasis

(8) Large Zone / High Gray Emphasis

(9) Gray-Level Non-Uniformity

(10) Zone Size Non-Uniformity

(11) Zone Size Percentage

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • thiele插值算法 1点插值算法 function [C,c]=thiele(X,Y,Z)%X为插值点横坐标,Y...
    00crazy00阅读 1,881评论 0 4
  • 不同图像灰度不同,边界处一般会有明显的边缘,利用此特征可以分割图像。需要说明的是:边缘和物体间的边界并不等同,边缘...
    大川无敌阅读 13,623评论 0 29
  • 本章介绍了基于elastix的基本配准概念。 更高级的配准主题将在第6章中讨论。图像配准是医学影像领域的重要工具。...
    peterpan_hai阅读 8,680评论 1 10
  • 20160328-允雯允武-0216 为政第2则,论语学习第18天 【原文】 子曰:“诗三百,一言以蔽之,曰:‘思...
    允雯允武阅读 158评论 0 0
  • 大家好,我叫毛竹,无中生有智慧的实验者,小微大有平台CEO。用十个字来介绍一下我自己:毛竹,扎根三年,一日长成!是...
    毛竹2017阅读 838评论 1 0