Форма листа растения и ее цифровая модель

С развитием компьютерной техники некогда решаемые традиционными методами задачи классификации в биологии претерпели существенные изменения. Так, например, классификация объектов по какому-либо набору морфометрических признаков, теперь допускает свое развитие с использованием статистических методов, позволяющих учесть особенности строения объекта без апелляции к конкретным признакам, а к его модели формы. Речи идет о методах геометрической морфометрии, когда на основе выполненных измерений (или полученных цифровых данных) строится модель (например, выбирается набор характерных точек, точек максимальной кривизны, точек пересечения главной оси инерции объекта с линией его контура и т.д.), и классифицирующий алгоритм (либо комплекс статистических методов) применяется уже к полученным моделям формы объекта. 

При традиционных подходах геометрической морфометрии, первым этапом является выбор базисных точек, локализацию которых можно легко определить для всей совокупности анализируемых объектов. Например, в случае, если анализируется совокупность гербарных образцов  листьев каких-либо видов растений, базисными точками, описывающими форму объекта могут быть, например, точки, расстояние между которыми максимально. Эти точки будут характеризовать "длину" объекта; в отношении другой пары базисных точек можно потребовать, чтобы расстояние между ними было также максимально, но отрезок, соединяющий их, был перпендикулярен отрезку, характеризующему длину объекта. Построенная таким образом пара точек будет характеризовать "ширину" объекта. Следующим этапом геометрического подхода является сравнение положений базисных точек; таким образом, эти точки выступают в качестве характеристик формы объекта или представляют его модель.

Если говорить о  биологических объектах, то последние могут сильно отличаться в размерах, без существенных различий по форме. Поэтому важным этапом геометрического подхода является приведение всех таких наборов базисных точек к единому масштабу,  а также их оптимальной ориентации в пространстве.

Другой подход к сравнению форм (здесь речь идет о кривых на плоскости) заключается в выполнении Фурье анализа специально построенной параметрической функции, описывающей контур исследуемого объекта. При этом нередко используется подход, именуемый "эллиптический" Фурье анализ (Elliptic Fourier Transform). 

Данные подходы геометрической морфометрии — используют модели формы природного объекта различной детализации: в случае использования базисных точек моделью формы является совокупность последних, в другом случае  параметрическая функция, описывающая контур. 

Создание моделей формы для последующего анализа  необходимый этап для сравнения форм природных объектов. С этой целью ниже рассматривается Python класс, для детального описания контуров и ряд полезных методов этого класса, позволяющих вычислять некоторые морфометрические характеристики анализируемых контуров. 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
class ContourLeaf(object):
    '''
    Basic class to handle leaf contour.
    '''
    
    def __str__(self):
        return 'Contour leaf object: nodes: %s, dpc: %s' %(len(self.points),self.dpc)
    
    def __init__(self,dpc,points,description=None):
        self.dpc=dpc
        self.points=np.array(points,dtype=np.float64)
        self.description=description
        self.relpoints=None
        self.pcapoints=None
        self.datasource=None
   
        #Properties of contours
        self._length=None
        self._area=None
        self._curvatures=None
        self._inertia=None
        self._maxminxy=None
        
    @property
    def area(self):
        'Area of the contour'
        return self._area

    @area.getter
    def area(self):
        from shapely.geometry import Polygon
        if  self.pcapoints==None:
            self.interpolated(self)
        try:
            polygon=Polygon(self.ppts)
            self._area=polygon.area
        except:
            self._area=None
        return self._area
    
    @property
    def length(self):
        'Length of the contour'
        pass
    
    @length.getter
    def length(self):
        from shapely.geometry import Polygon
        if  self.pcapoints==None:
            self.interpolated(self)
        try:
            polygon=Polygon(self.ppts)
            self._length=polygon.length
        except:
            self._length=None
        return self._length
    
    @property
    def inertia(self):
        pass
    
    @inertia.getter
    def inertia(self):
        if  self.pcapoints==None:
            self.interpolated(self)
        vals=[np.sum(np.array(self.pcapoints[0])**2),np.sum(np.array(self.pcapoints[1])**2)]
        self._inertia=(max(vals),min(vals))
        return self._inertia
    
    @property
    def coordinates(self):
        pass
    
    @coordinates.getter
    def coordinates(self):
        if self.pcapoints==None:
            self.interpolated(self)
        else:
            pass
        return np.array(self.pcapoints)
           
    @property
    def maxminxy(self):
        pass

    @maxminxy.getter
    def maxminxy(self):
        if  self.pcapoints==None:
            self.interpolated(self)
        self._maxminxy = (min(self.pcapoints[0]), max(self.pcapoints[0]), \
                          min(self.pcapoints[1]), max(self.pcapoints[1]))
        return self._maxminxy
    
    @property
    def position(self):
        if  self.pcapoints==None:
            self.interpolated(self)
        indleft = np.argmin(self.pcapoints[1])
        indright = np.argmax(self.pcapoints[1])
        valleft = self.pcapoints[0][indleft]
        valright = self.pcapoints[0][indright]
        return ((valleft+valright)*0.5 - \
                (self.maxminxy[1]+self.maxminxy[0])*0.5)/(self.maxminxy[1]-self.maxminxy[0])
    
    @property
    def curvatures(self):
        pass
    
    @curvatures.getter
    def curvatures(self):
        from scipy import interpolate
        if  self.pcapoints==None:
            self.interpolated(self)
        try:
            tck,unew = interpolate.splprep([np.array(self.ppts)[:,0],np.array(self.ppts)[:,1]], s=0.0)
            dr=np.array(interpolate.splev(unew,tck,der=1)).transpose()
            ddr=np.array(interpolate.splev(unew,tck,der=2)).transpose()
            curvature=[np.linalg.norm(np.cross(x,y))/np.linalg.norm(x)**3 for x,y in zip(dr,ddr)]
            self._curvatures=[min(curvature),max(curvature),np.mean(curvature),np.std(curvature),\
                              np.mean(curvature[int(len(curvature)/2.0)-\
                                                int(len(curvature)/8.0):int(len(curvature)/2.0)+\
                                                int(len(curvature)/8.0)])]

Для использования данного класса необходимо, чтобы были установлены пакеты SciPy, Shapely и Scikit-learn. При создании объекта класса ContourLeaf на вход функции конструктора __init__ подается набор точек  массив размерностью Nx2, где N  количество точек, полученных в резульате оцифровки контура.

Поскольку эти точки могут быть результатом ручной оцифровки листа по его фотографии/скану, они могут располагаться неравномерно. Более того, если условия сканирования и/или фотографирования не остаются постоянными, меняется и разрешение изображений, а, следовательно, количество пикселей, содержащихся, например, в 1 см, также не будет постоянным. Для того, чтобы впоследствии перейти в модели контура листа к привычным единицам измерения (допустим, сантиметрам, а не пикселям), необходимо знать сколько пикселей для каждого изображения содержится в одном сантиметре. Конечно, если планируется пользоваться только безразмерными характеристиками формы (таким как отношение линейных размеров объекта, например, длины и ширины и пр.), как собственно и для методов геометрической морфометрии, можно оставить измерения пикселях; однако это приведет к потере информации о размерах объекта, что может быть не очень удобно в перспективе.  

Этот дополнительный и крайне важный параметр – количество пикселей в одном сантиметре длины необходимо подавать на вход функции конструктора __init__. Это dpc - (dots per centimeter) - количество точек в одном сантиметре длины. Здесь не рассматриваются технические вопросы, каким образом этот параметр может быть получен в автоматическом режиме. Задача определения данного параметра становится тривиальной, например, в случае сканирования изображений гербарных листов с постоянным разрешением сканирующего устройства. В этом случае достаточно знать сколько пикселей "используется" при сканировании одного сантиметра длины и это позволит узнать все характеристики объекта в привычных единицах измерения.

Алгоритм построения модели контура, заданного совокупностью точек, реализуется в предлагаемом классе следующим образом:

  • вычисляется барицентр (центр масс) совокупности опорных точек, поданных на вход функции конструктора __init__;
  • начало системы координат переносится в барицентр, происходит пересчет координат;
  • на основе данных о количестве точек в одном сантиметре длины (dpc) осуществляется перерасчет всех значений в пикселях в значения в сантиметрах;
  • ориентация системы координат выбирается на основе метода главных компонент, примененного к совокупности базовых точек (т.е. в качестве локальной барицентрической системы координат используются главные направления);
  • координаты опорных точек пересчитываются, учитывая, что система координат ориентирована по главным направлениям;
  • с использованием библиотеки scipy строится интерполяционная линия (B-сплайн), назначение которой  нивелировать неравномерность размещения опорных точек по контуру;
  • с использованием библиотеки shapely вычисляются некоторые морфометрические характеристики контура (площадь, длина контура и др);
  • при помощи B-сплайна (путем дифференцирования) вычисляется кривизна вдоль контура (максимальная, средняя и др);
  • дискретная модель контура представляется равномерно размещенным по контуру набором точек, координаты которых вычисляются на базе построенного B-сплайна;
  • непрерывная модель контура, собственно, B-сплайн.

Пример использования  класса ContourLeaf для построения моделей листьев растений приводится на следующем рисунке.

Пример цифровых моделей нескольких листьев рододендрона
Приводимые контура листьев содержат 1000 равномерно размещенных точек, принадлежащих B-сплайну,
построенного по набору опорных точек (опорные точки получены в результате ручной оцифровки фотографий гербарных листов);

Поскольку ось абсцисс это главная ось, она представляет собой также ось, относительно которой момент инерции листа минимален; таким образом, выбирается в определенном смысле "однородная" ориентация всех листьев, независимо от их расположения на исходных изображениях.

Работающий пример, с файлами, содержащими результаты ручной оцифровки контуров, можно загрузить перейдя по ссылке: 

Пример работы с оцифрованным контурами листьев на Python (1,8 MB)

blog comments powered by Disqus