public Rib(Versh first, Versh second, double dist) { _firstV = first; _secondV = second; _dist = dist; index = 0; }
// метод, задающий координату Z всему сегменту public void setZ(Versh ourSegm, int z) { foreach (Versh v in _v2d) { if (v.Root == ourSegm.Root) { v._z = z; } } }
public void MergeSegment(Versh v) { //проверяем, если уже один сегмент if (this.Root == v.Root) { return; } VershCount += v.VershCount; v.Root = this.Root; }
// Нахождение заднего фона изображения public void FindBackground() { // Найдём самый большой сегмент, который касается верхнего края Versh Background = FindLargeSegment(0); // Т.к. это фон, координата z максимально возможно отдалена от пользователя int z = zCoordinates.Length; // Зададим z координату всем вершинам сегмента setZ(Background, z); }
public Versh(int x1, int y1, byte r, byte g, byte b) { _count = 1; _row = x1; _column = y1; _r = r; _g = g; _b = b; _parent = null; _maxDist = 0; }
// принадлежит методу сверху, ищет самый больший перепад в сегменте private static void GetMaxDist(Versh root) { for (int i = ribs.Count - 1; i >= 0; i--) { //если оба принадлежат тому сегменту, который мы только что слепили - т.е. если у них тот же корень //то это ребро нам подходит if (ribs[i]._firstV.Root == root && ribs[i]._secondV.Root == root) { root.MaxDist = ribs[i]._dist; break; } } }
// Нахождение самого большого сегмента в определённом ряду // Пока работает только для строк public Versh FindLargeSegment(int range) { int max = 1; Versh maxSegm = _v2d[range, 0]; for (int x = 0; x < _width; x++) { if (_v2d[range, x].VershCount > max) { max = _v2d[range, x].VershCount; maxSegm = _v2d[range, x]; } } return(maxSegm.Root); }
// очень умная шикарная штука, но она осложнила программу и добавила неточностей( private void MergeSegments() { //теперь объединяем уже существующие сегменты по какой-то хитрой формуле - МАГИЯ double ksegm = 2; for (int k = 0; k < ribs.Count; k++) { Rib r = ribs[k]; //если у них один корень, то они уже в одном сегменте - тогда их не надо соединять снова if (r._firstV.Root == r._secondV.Root) { continue; } //если же они в разных сегментах, то мы должны оценить, должны ли мы соединять сегменты //одинокий пиксель - сегмент из одной вершины //поправка от количества пикселей double t1 = ksegm / r._firstV.VershCount; double t2 = ksegm / r._secondV.VershCount; //перепады внутри сегментов с поправкой от количества пикселей внутри сегмента double m1 = r._firstV.MaxDist + t1; double m2 = r._secondV.MaxDist + t2; //меньшая из больших дистанций внутри сегментов, с поправкой (та которая с количеством пикселей) double minMax = Math.Min(m1, m2); if (r._dist < minMax) { r._firstV.MergeSegment(r._secondV); Versh root = r._firstV.Root; //после объединения ищем новый наибольший перепад //т.к. рёбра упорядочены по возрастанию веса, а мы идём с конца - первое попавшееся ребро из нашего сегмента будет самым большим GetMaxDist(root); } } }
public List <AreaContainer> FormAreas() { List <AreaContainer> result = new List <AreaContainer>(); if (_v2d == null) { Segmentation segm = new Segmentation(_photo); segm.SortRebr(); segm.Segment(); _v2d = Segmentation.v2d; //Полученный битмап нам неинтересен, только массив } AreaContainer currentArea = new AreaContainer(); // Сначала ищем землю // Находим самый большой сегмент на нижней строке (его корень) Versh ground = FindLargeSegment(_height - 1); int pixelRow = 0; int pixelColumn = 0; for (int i = 0; i < _width; i++) { if (_v2d[_height - 1, i].Root == ground.Root) { pixelRow = _height - 1; pixelColumn = i; break; } } // Формируем границу земли currentArea.Borders = FindBorder(pixelRow, pixelColumn); // Формируем всю область земли FormSingleArea(pixelRow, pixelColumn); // Добавляем землю в список областей - у неё будет индекс 0 result.Add(currentArea); // Потом найдём небо currentArea = new AreaContainer(); Versh sky = FindLargeSegment(0); pixelRow = 0; pixelColumn = 0; for (int i = 0; i < _width; i++) { if (_v2d[0, i].Root == sky.Root) { pixelRow = 0; pixelColumn = i; break; } } // Формируем границу неба currentArea.Borders = FindBorder(pixelRow, pixelColumn); // Формируем всю область неба FormSingleArea(pixelRow, pixelColumn); // Добавляем небо в список областей - у него будет индекс 1 result.Add(currentArea); // А теперь, дамы и господа, добавляем всё остальное for (int row = 0; row < _height; row++) { for (int column = 0; column < _width; column++) { bool alreadyDone = false; if (visited[row, column] == true) { continue; } // Чтобы избавиться от багов и мелких отростков пикселей int svoi = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (row + i < 0 || row + i >= _height || column + j < 0 || column + j >= _width) { continue; } if (_v2d[row, column].Root == _v2d[row + i, column + j].Root) { svoi++; } } } if (svoi < 5) { continue; } currentArea = new AreaContainer(); // Ищет граничные пиксели currentArea.Borders = FindBorder(row, column); // Ищет все пиксели области FormSingleArea(row, column); // Добавляет в лист листов, где границы всех областей result.Add(currentArea); } } return(result); }
// Находим поверхность ("землю") 3д модели, а также максимальное количество слоёв public List <Versh> FindBorder(int firstRow, int firstColumn) { while (_v2d[firstRow, firstColumn].isBorderVersh(_height, _width) == false) { firstRow--; } int[,] steps = { { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 } }; List <Versh> borders = new List <Versh>(); List <Versh> trash = new List <Versh>(); borders.Add(_v2d[firstRow, firstColumn]); Versh first = _v2d[firstRow, firstColumn]; Versh current = null; for (int k = 0; k < 8; k++) { int secondColumn = firstColumn + steps[k, 1]; int secondRow = firstRow + steps[k, 0]; if (secondColumn < 0 || secondColumn >= _width || secondRow < 0 || secondRow >= _height) { continue; } if (_v2d[secondRow, secondColumn].Root == first.Root && _v2d[secondRow, secondColumn].isBorderVersh(_height, _width) && _v2d[secondRow, secondColumn] != first) { current = _v2d[secondRow, secondColumn]; borders.Add(current); break; } } while (current != first) { bool foundNext = false; int currentRow = current._row; int currentColumn = current._column; for (int k = 0; k < 8; k++) { int newRow = currentRow + steps[k, 0]; int newColumn = currentColumn + steps[k, 1]; if (newRow == first._row && newColumn == first._column) { return(borders); } if (newColumn < 0 || newColumn >= _width || newRow < 0 || newRow >= _height) { continue; } if (_v2d[newRow, newColumn].Root == first.Root) { if (_v2d[newRow, newColumn].isBorderVersh(_height, _width)) { if (borders.Contains(_v2d[newRow, newColumn]) == false) { if (trash.Contains(_v2d[newRow, newColumn]) == false) //для выхода из тупиков { current = _v2d[newRow, newColumn]; borders.Add(current); foundNext = true; break; } } } } } if (foundNext == false) { trash.Add(borders[borders.Count - 1]); borders.RemoveAt(borders.Count - 1); current = borders[borders.Count - 1]; } } // // Создадим землю 3д модели // for (int k = lastY, range = 0; k >= firstY; k--, range++) // { // for (int x = 0; x < _width; x++) // { // // Оптимизировать?? // if (_v2d[k, x].Root == Ground.Root) // _v2d[k, x]._z = range; // } // } return(borders); }
public void SortRebr() { int index = 0; for (int i = 0; i < _height; i++) { for (int j = 0; j < _width; j++) { r[i, j] = _photo[index++]; g[i, j] = _photo[index++]; b[i, j] = _photo[index++]; } } v2d = new Versh[_height, _width]; for (int x = 0; x < _height; x++) { for (int y = 0; y < _width; y++) { v2d[x, y] = new Versh(x, y, r[x, y], g[x, y], b[x, y]); } } int[,] rebr = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 0 }, { 2, 0 }, { 3, 0 } }; for (int x = 0, limitX = _height - 3; x < limitX; x++) { for (int y = 0, limitY = _width - 3; y < limitY; y++) { for (int i = 0; i < 6; i++) { int positionX = x + rebr[i, 0]; int positionY = y + rebr[i, 1]; //тут мы знаем, что следующий пиксель существует, потому что границы в цикле ширина-3, высота-3 CreateRib(x, y, positionX, positionY, r, g, b, v2d, ribs); } } for (int y = _width - 3; y < _width; y++) { for (int i = 0; i < 6; i++) { int positionX = x + rebr[i, 0]; int positionY = y + rebr[i, 1]; // если следующий пиксель не существует if ((positionX >= _height) || (positionY >= _width)) { continue; } CreateRib(x, y, positionX, positionY, r, g, b, v2d, ribs); } } } for (int x = _height - 3; x < _height; x++) { for (int y = 0; y < _width; y++) { for (int i = 0; i < 6; i++) { int positionX = x + rebr[i, 0]; int positionY = y + rebr[i, 1]; // если следующий пиксель не существует if ((positionX >= _height) || (positionY >= _width)) { continue; } CreateRib(x, y, positionX, positionY, r, g, b, v2d, ribs); } } } // сортировка по дистанции (цене ребра) ribs.Sort((first, second) => first._dist.CompareTo(second._dist)); foreach (Versh versh1 in v2d) { versh.Add(versh1); } }