- •1. Программные системы и методы 3D-реконструкции биомедицинских данных
- •1.1. Обзор программных комплексов
- •1.2. Обзор основных подходов
- •1.2.1. Структуры визуализируемых объёмов
- •1.2.2. Сегментация и идентификация объектов
- •1.2.3. Построение поверхностей объектов
- •2. Модели, методы и алгоритмы, положенные в основу сегментации и поиска объектов
- •2.1. Формальная постановка задачи и основные этапы её решения
- •2.2. Сегментация данных компьютерной томографии и электронной микроскопии
- •2.3. Повышение производительности вычислений в методе k-средних
- •2.4. Метод иерархической декомпозиции сегментов
- •2.5. Морфологическая фильтрация сегментов
- •2.6. Построение признакового описания сегмента
- •2.7. Поиск объектов методом динамического программирования
- •3. Описание реализации программной системы
- •3.1. Основные классы, структуры и методы
- •3.2. Организация пользовательского интерфейса
- •Заключение
- •Список литературы
- •Приложение А. Примеры результатов сегментации и идентификации объектов
- •Приложение Б. Классы, реализующие семейство методов k-средних
- •Приложение В. Исходные коды базовых компонентов программной системы
- •В.1. Основные классы и структуры для хранения и обработки данных
- •В.2. Методы вычисления атрибутов сегментов
- •В.3. Поиск объектов по найденным сегментам
Приложение В. Исходные коды базовых компонентов программной системы
В.1. Основные классы и структуры для хранения и обработки данных
Листинг В.1: Объявления структур и классов
#pragma once
#include <vector> #include "opencv/cv.h"
enum SEGMENT_TYPE {DENSITY, SPATIAL, CONNECTED};
struct |
TLayer |
|
// слой данных (двумерное изображение) |
||
{ |
*Density; |
|
|
|
|
short |
// |
ширина и высота |
слоя |
||
size_t sizeX, |
sizeY; |
||||
float |
scaleX, |
scaleY; |
// |
размеры слоя по |
осям |
// Конструкторы
TLayer() : Density(0), sizeX(0), sizeY(0), scaleX(0), scaleY(0) {}; TLayer(short*_density, size_t _sizeX, size_t _sizeY, float _scaleX, float _scaleY):
Density(_density), sizeX(_sizeX), sizeY(_sizeY), scaleX(_scaleX), scaleY(_scaleY) {};
//приведённый индекс воксела в наборе данных size_t ReducedIndex(size_t x, size_t y);
//Методы доступа к данным в наборе
short GetDensity(size_t index);
short GetDensity(size_t x, size_t y);
// методы, вычисляющие абсолютную разницу между вокселами
float GetDistance(const cv::Point_<size_t> &p1, const cv::Point_<size_t> &p2); float GetDistance(size_t i, size_t j);
};
struct TVoxelSegments // индексы сегментов, которым принадлежит воксел
{
//индексы сегментов по плотности int SegmentIndex_2D, SegmentIndex_3D;
//индексы пространственных кластеров, которым принадлежит воксел int SpatialSegmentIndex_2D, SpatialSegmentIndex_3D;
//индекс компоненты связности, входящей в сегмент, которому принадлежит воксел int ComponentIndex_2D;
//Конструктор по умолчанию
TVoxelSegments(): SegmentIndex_2D(-1), SegmentIndex_3D(-1), SpatialSegmentIndex_2D(-1), SpatialSegmentIndex_3D(-1), ComponentIndex_2D(-1) {}
};
struct TColor // Цвет воксела или сегмента
{
float R, G, B, A; // параметры цвета
// Конструкторы
59
TColor():R(0), G(0), B(0), A(0) {};
TColor(float value_R, float value_G, float value_B, float value_A): R(value_R), G(value_G), B(value_B), A(value_A) {};
TColor(float value_R, float value_G, float value_B): R(value_R), G(value_G),
}; |
B(value_B) {}; |
|
|
struct TPath |
// путь (цепочка) из сегментов |
{ |
// корень цепочки |
cv::Vec3i root; |
|
std::vector <cv::Vec3i> forward; |
// передний фрагмент цепочки |
std::vector <cv::Vec3i> backward; |
// задний фрагмент цепочки |
// Конструктор по умолчанию
TPath():root(cv::Vec3i(0, 0, 0)) { forward.clear(); backward.clear(); }
}; |
|
class TVoxelsData |
// исходные данные (воксельная модель) |
{ |
|
public: |
// плотность вокселов |
short *Density; |
|
bool *IsVoxelUsed; |
// маска используемых вокселов |
// сегменты, которым принадлежат вокселы |
|
TVoxelSegments *VoxelSegments; |
|
size_t sizeX, sizeY, sizeZ; |
// размеры вдоль координатных осей |
float scaleX, scaleY, scaleZ; |
// масштабы вдоль координатных осей |
size_t TotalSize; |
// размер всего набора вокселов |
// минимальное и максимальное значение плотности |
|
short MinDensity, MaxDensity; |
// дисперсия плотности |
float DensityVariance; |
|
float XVariance, YVariance; |
// дисперсии по координатам |
// Конструкторы и деструктор
TVoxelsData();
TVoxelsData(const char *pathToBinFile); TVoxelsData(const TLayer &layer); ~TVoxelsData();
void Init(); // инициализация всех полей значениями по умолчанию void Clear(); // освобождение всех ресурсов, занятых данными
//Методы загрузки данных из файла bool Load(const char *pathToBinFile);
bool Load(const char *pathToBinFile, size_t z_start, size_t nLayers);
//Методы получения подмножества исходных данных
TVoxelsData* GetSubData(size_t z_first, size_t z_last);
TLayer GetLayer(size_t z);
//приведённый индекс воксела в наборе данных size_t ReducedIndex(size_t x, size_t y, size_t z);
//Методы доступа к данным в наборе
short GetDensity(size_t index);
short GetDensity(size_t x, size_t y, size_t z);
// Методы, вычисляющие абсолютную разницу между вокселами float GetDistance(const cv::Point3_<size_t> &p1,
const cv::Point3_<size_t> &p2);
60
float GetDistance(size_t i, size_t j);
//Методы, вычисляющие дисперсию плотности и координат вокселов void CalcDensityVariance();
void CalcXYVariance();
//Поиск связных областей
int FindConnectedRegions(size_t LayerIndex, cv::Vec2i SegmentIndex);
}; |
|
|
struct |
TSegment |
// сегмент из вокселов |
{ |
MinDensity; |
// минимальное значение плотности |
short |
||
short |
MaxDensity; |
// максимальное значение плотности |
TColor Color; |
// цвет сегмента |
|
bool Visible; |
// атрибут видимости вокселов, входящих в сегмент |
|
TColor tmpColor; |
// импортированный цвет сегмента |
|
bool tmpVisible; |
// импортированный атрибут видимости |
|
float |
MeanX, MeanY; |
// геометрический центр сегмента |
float |
DevX, DevY; |
// среднеквадратическое отклонение вдоль координатных осей |
float |
MeanDensity; |
// среднее значение плотности |
float |
DensityDev; |
// разброс вокселов сегмента по плотности |
float |
Volume; |
// объём сегмента (с учётом размеров воксела вдоль осей) |
//сегменты следующего уровня иерархии std::vector <TSegment>* NextLevelSegments;
//Конструктор сегмента по умолчанию
TSegment(): MinDensity(0), MaxDensity(0), Histogram(0), NextLevelSegments(0),
Color(TColor(0.0f, 0.0f, 0.0f, 0.0f)), Visible(false), tmpColor(TColor(0.0f, 0.0f, 0.0f, 0.0f)), tmpVisible(false) {}
/* Рассогласование с другим сегментом (flags – признаки использования/неиспользования атрибутов, deviation – массив среднеквадратических отклонений атрибутов) */
double DistanceTo(const TSegment &segment,
const bool* flags, const double* deviation);
// Проверка на смежность с другим сегментом по диапазону значений плотности bool IsAdjacentWith(const TSegment &segment);
};
Листинг В.2: Реализация структур и классов
#include "Structures.h"
#include "opencv2/imgproc/imgproc.hpp" #include <fstream>
using namespace cv; using namespace std;
#pragma region TLayer methods
short TLayer::GetDensity(size_t index)
{
return Density[index];
}
short TLayer::GetDensity(size_t x, size_t y)
{
return Density[ReducedIndex(x, y)];
}
61
size_t TLayer::ReducedIndex(size_t x, size_t y) { return x + sizeX*y; }
float TLayer::GetDistance(const Point_<size_t> &p1, const Point_<size_t> &p2)
{
return fabs((float)(Density[ReducedIndex(p1.x, p1.y)] - Density[ReducedIndex(p2.x, p2.y)]));
}
float TLayer::GetDistance(size_t i, size_t j)
{ return fabs((float)(Density[i] - Density[j])); }
#pragma endregion
#pragma region TVoxelsData methods
void TVoxelsData::Init()
{
sizeX = sizeY = sizeZ = TotalSize = 0; scaleX = scaleY = scaleZ = 0.0f; MinDensity = MaxDensity = 0;
Density = NULL; IsVoxelUsed = NULL; VoxelSegments = NULL;
}
void TVoxelsData::Clear()
{
delete [] Density; delete [] IsVoxelUsed; delete [] VoxelSegments;
Init();
}
bool TVoxelsData::Load(const char* pathToBinFile)
{
ifstream fs(pathToBinFile, std::ios::in | std::ios::binary);
if (!fs) return false;
fs.read((char*)&sizeX, sizeof(int)); fs.read((char*)&sizeY, sizeof(int)); fs.read((char*)&sizeZ, sizeof(int));
TotalSize = sizeX*sizeY*sizeZ;
fs.read((char*)&scaleX, sizeof(float)); fs.read((char*)&scaleY, sizeof(float)); fs.read((char*)&scaleZ, sizeof(float));
Density = new short [TotalSize];
fs.read((char*)Density, sizeof(short) * TotalSize);
fs.close();
VoxelSegments = new TVoxelSegments [TotalSize];
IsVoxelUsed = NULL;
return true;
}
62
bool TVoxelsData::Load(const char* pathToBinFile,
size_t StartLayer, size_t nLayers)
{
ifstream fs(pathToBinFile, std::ios::in | std::ios::binary);
if (!fs) return false;
fs.read((char*)&sizeX, sizeof(int)); fs.read((char*)&sizeY, sizeof(int)); fs.read((char*)&sizeZ, sizeof(int));
TotalSize = nLayers*sizeX*sizeY;
fs.read((char*)&scaleX, sizeof(float)); fs.read((char*)&scaleY, sizeof(float)); fs.read((char*)&scaleZ, sizeof(float));
for (size_t i = 0; i < StartLayer; ++i)
{
Density = new short [sizeX*sizeY]; fs.read((char*)Density, sizeof(short) * sizeX * sizeY); delete [] Density;
Density = NULL;
}
Density = new short [TotalSize]; fs.read((char*)Density, sizeof(short)*TotalSize); fs.close();
return true;
}
TVoxelsData::TVoxelsData() { Init(); }
TVoxelsData::TVoxelsData(const char* pathToBinFile) { Load(pathToBinFile); }
TVoxelsData::TVoxelsData(const TLayer &layer)
{
sizeX = layer.sizeX; sizeY = layer.sizeY; sizeZ = 1; TotalSize = sizeX*sizeY*sizeZ;
scaleX = layer.scaleX; scaleY = layer.scaleY; Density = layer.Density;
VoxelSegments = new TVoxelSegments [TotalSize]; IsVoxelUsed = NULL;
}
TVoxelsData::~TVoxelsData() { Clear(); }
TVoxelsData* TVoxelsData::GetSubData(size_t z_first, size_t z_last)
{
TVoxelsData* result = new TVoxelsData();
result->Density = Density + z_first*sizeX*sizeY; result->IsVoxelUsed = NULL; result->VoxelSegments = NULL;
result->sizeX = sizeX; result->sizeY = sizeY; result->sizeZ = z_last-z_first+1;
TotalSize = sizeX*sizeY*sizeZ;
result->scaleX = scaleX; result->scaleY = scaleY;
63
result->scaleZ = scaleZ;
result->MinDensity = MinDensity; result->MaxDensity = MaxDensity;
return result;
}
TLayer TVoxelsData::GetLayer(size_t z) { return TLayer(Density + z*sizeX*sizeY, sizeX, sizeY, scaleX, scaleY); }
size_t TVoxelsData::ReducedIndex(size_t x, size_t y, size_t z) { return x + y*sizeX + z*sizeX*sizeY; }
short TVoxelsData::GetDensity(size_t index) { return Density[index]; } short TVoxelsData::GetDensity(size_t x, size_t y, size_t z) { return Density[ReducedIndex(x, y, z)]; }
float TVoxelsData::GetDistance(size_t i, size_t j) { return fabs((float)(Density[i] - Density[j])); }
float TVoxelsData::GetDistance(const cv::Point3_<size_t> &p1, const cv::Point3_<size_t> &p2)
{
return fabs((float)(GetDensity(p1.x, p1.y, p1.z)-GetDensity(p2.x, p2.y, p2.z)));
}
void TVoxelsData::CalcDensityVariance()
{
cv::Mat mat(TotalSize, 1, CV_16S, Density); cv::Mat tmp; multiply(mat, mat, tmp);
double ff = mean(mat)[0], fff = mean(tmp)[0]; DensityVariance = (float)(fff-ff*ff);
}
void TVoxelsData::CalcXYVariance()
{
float mean_x = (sizeX-1)/2.0f, mean_y = (sizeY-1)/2.0f;
float sqr_x = (sizeX-1)*(2*sizeX-1)/6.0f, sqr_y = (sizeY-1)*(2*sizeY-1)/6.0f; XVariance = sqr_x - mean_x*mean_x, YVariance = sqr_y - mean_y*mean_y;
}
int TVoxelsData::FindConnectedRegions(size_t LayerIndex, Vec2i SegmentIndex)
{
int Label = -1;
vector <int> TmpComponentIndex; TmpComponentIndex.clear();
for (size_t i = 0; i < sizeX*sizeY; ++i)
{
TmpComponentIndex.push_back( VoxelSegments[i+LayerIndex*sizeX*sizeY].ComponentIndex_2D); VoxelSegments[i+LayerIndex*sizeX*sizeY].ComponentIndex_2D = -1;
}
for (size_t j = 0; j < sizeY; ++j) for (size_t i = 0; i < sizeX; ++i)
if ((VoxelSegments[ReducedIndex(i, j, LayerIndex)].SegmentIndex_2D == SegmentIndex[0]) && (VoxelSegments[ReducedIndex(i, j, LayerIndex)].SpatialSegmentIndex_2D == SegmentIndex[1]))
{
if (i && (VoxelSegments[ReducedIndex(i-1, j, LayerIndex)].ComponentIndex_2D >= 0))
64
{
VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D = VoxelSegments[ReducedIndex(i-1, j, LayerIndex)].ComponentIndex_2D;
}
else
if (j && (VoxelSegments[ReducedIndex(i, j-1, LayerIndex)].ComponentIndex_2D >= 0))
{
VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D = VoxelSegments[ReducedIndex(i, j-1, LayerIndex)].ComponentIndex_2D;
}
else
if (j && (i < sizeX-1) && (VoxelSegments[ReducedIndex(i+1, j-1, LayerIndex)].ComponentIndex_2D >= 0))
{
VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D = VoxelSegments[ReducedIndex(i+1, j-1, LayerIndex)].ComponentIndex_2D;
}
else
if (i && j && (VoxelSegments[ReducedIndex(i-1, j-1, LayerIndex)].ComponentIndex_2D >= 0))
{
VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D = VoxelSegments[ReducedIndex(i-1, j-1, LayerIndex)].ComponentIndex_2D;
}
else
{
VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D = (++Label);
}
}
int nRegions = Label+1;
size_t* distance = new size_t [nRegions];
for (int label = 0; label < nRegions; ++label) distance[label] = 0;
for (size_t j = 0; j < sizeY; ++j) for (size_t i = 0; i < sizeX; ++i) if ((VoxelSegments[ReducedIndex(i, j,
LayerIndex)].SegmentIndex_2D == SegmentIndex[0]) && (VoxelSegments[ReducedIndex(i, j,
LayerIndex)].SpatialSegmentIndex_2D == SegmentIndex[1]))
{
if (i && (j < sizeY-1) && (VoxelSegments[ReducedIndex(i-1, j+1, LayerIndex)].ComponentIndex_2D >= 0) && (VoxelSegments[ReducedIndex(i-1, j+1, LayerIndex)].ComponentIndex_2D != VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D))
{
int component_index = VoxelSegments[ReducedIndex(i-1, j+1, LayerIndex)].ComponentIndex_2D;
for (size_t index = 0; index < sizeX*sizeY; ++index)
if (VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D == component_index)
VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D =
65
VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D;
for (int label = component_index + 1; label < nRegions; ++label) distance[label]++;
Label--;
}
else
if ((j < sizeY-1) && (VoxelSegments[ReducedIndex(i, j+1, LayerIndex)].ComponentIndex_2D >= 0) && (VoxelSegments[ReducedIndex(i, j+1, LayerIndex)].ComponentIndex_2D != VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D))
{
int component_index = VoxelSegments[ReducedIndex(i, j+1, LayerIndex)].ComponentIndex_2D;
for (size_t index = 0; index < sizeX*sizeY; ++index)
if (VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D == component_index)
VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D = VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D;
for (int label = component_index + 1; label < nRegions; ++label) distance[label]++;
Label--;
}
else
if ((i < sizeX-1) && (j < sizeY-1) && (VoxelSegments[ReducedIndex(i+1, j+1, LayerIndex)].ComponentIndex_2D >= 0) && (VoxelSegments[ReducedIndex(i+1, j+1, LayerIndex)].ComponentIndex_2D != VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D))
{
int component_index = VoxelSegments[ReducedIndex(i+1, j+1, LayerIndex)].ComponentIndex_2D;
for (size_t index = 0; index < sizeX*sizeY; ++index)
if (VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D == component_index)
VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D = VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D;
for (int label = component_index + 1; label < nRegions; ++label) distance[label]++;
Label--;
}
else
if ((i < sizeX-1) && (VoxelSegments[ReducedIndex(i+1, j, LayerIndex)].ComponentIndex_2D >= 0) && (VoxelSegments[ReducedIndex(i+1, j, LayerIndex)].ComponentIndex_2D != VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D))
{
int component_index = VoxelSegments[ReducedIndex(i+1, j, LayerIndex)].ComponentIndex_2D;
66
for (size_t index = 0; index < sizeX*sizeY; ++index)
if (VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D == component_index)
VoxelSegments[index+LayerIndex*sizeX*sizeY].ComponentIndex_2D = VoxelSegments[ReducedIndex(i, j, LayerIndex)].ComponentIndex_2D;
for (int label = component_index + 1; label < nRegions; ++label) distance[label]++;
Label--;
}
}
for (size_t i = 0; i < sizeX*sizeY; ++i)
{
if (TmpComponentIndex[i] >= 0) VoxelSegments[i+LayerIndex*sizeX*sizeY].ComponentIndex_2D = TmpComponentIndex[i];
else
if (VoxelSegments[i+LayerIndex*sizeX*sizeY].ComponentIndex_2D >= 0) VoxelSegments[i+LayerIndex*sizeX*sizeY].ComponentIndex_2D -= distance[VoxelSegments[i+LayerIndex*sizeX*sizeY].ComponentIndex_2D];
}
return Label+1;
}
#pragma endregion
#pragma region TSegment methods
double TSegment::DistanceTo(const TSegment &segment, const bool* flags, const double* deviation)
{
cv::Mat l(7, 1, CV_32FC1), r(7, 1, CV_32FC1);
l.at<float>(0, 0) = flags[0] ? MeanDensity/deviation[0] : 0; l.at<float>(1, 0) = flags[1] ? DensityDev/deviation[1] : 0; l.at<float>(2, 0) = flags[2] ? MeanX/deviation[2] : 0; l.at<float>(3, 0) = flags[3] ? MeanY/deviation[3] : 0; l.at<float>(4, 0) = flags[4] ? DevX/deviation[4] : 0; l.at<float>(5, 0) = flags[5] ? DevY/deviation[5] : 0; l.at<float>(6, 0) = flags[6] ? Volume/deviation[6] : 0;
r.at<float>(0, 0) = flags[0] ? segment.MeanDensity/deviation[0] : 0; r.at<float>(1, 0) = flags[1] ? segment.DensityDev/deviation[1] : 0; r.at<float>(2, 0) = flags[2] ? segment.MeanX/deviation[2] : 0; r.at<float>(3, 0) = flags[3] ? segment.MeanY/deviation[3] : 0; r.at<float>(4, 0) = flags[4] ? segment.DevX/deviation[4] : 0; r.at<float>(5, 0) = flags[5] ? segment.DevY/deviation[5] : 0; r.at<float>(6, 0) = flags[6] ? segment.Volume/deviation[6] : 0;
return cv::norm(l, r);
}
bool TSegment::IsAdjacentWith(const TSegment &segment)
{
return min(MaxDensity, segment.MaxDensity) >= max(MinDensity, segment.MinDensity);
}
#pragma endregion
67