/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkLabelObject_hxx #define itkLabelObject_hxx #include "itkLabelObjectLineComparator.h" #include "itkMath.h" #include namespace itk { template LabelObject::LabelObject() { m_Label = LabelType{}; m_LineContainer.clear(); } template auto LabelObject::GetAttributeFromName(const std::string & s) -> AttributeType { if (s == "Label") { return LABEL; } // can't recognize the name itkGenericExceptionMacro("Unknown attribute: " << s); } template std::string LabelObject::GetNameFromAttribute(const AttributeType & a) { switch (a) { case LABEL: return "Label"; } // can't recognize the namespace itkGenericExceptionMacro("Unknown attribute: " << a); } /** * Set/Get the label associated with that object. */ template auto LabelObject::GetLabel() const -> const LabelType & { return m_Label; } template void LabelObject::SetLabel(const LabelType & label) { m_Label = label; } /** * Return true if the object contain the given index and false otherwise. * Worst case complexity is O(L) where L is the number of lines in the object. */ template bool LabelObject::HasIndex(const IndexType & idx) const { auto end = m_LineContainer.end(); for (auto it = m_LineContainer.begin(); it != end; ++it) { if (it->HasIndex(idx)) { return true; } } return false; } template bool LabelObject::RemoveIndex(const IndexType & idx) { auto it = m_LineContainer.begin(); while (it != m_LineContainer.end()) { if (it->HasIndex(idx)) { IndexType orgLineIndex = it->GetIndex(); LengthType orgLineLength = it->GetLength(); if (orgLineLength == 1) { // remove the line and exit m_LineContainer.erase(it); return true; } if (orgLineIndex == idx) { // shift the index to the right and decrease the length by one ++orgLineIndex[0]; it->SetIndex(orgLineIndex); it->SetLength(orgLineLength - 1); return true; } else if (orgLineIndex[0] + static_cast(orgLineLength) - 1 == idx[0]) { // decrease the length by one it->SetLength(orgLineLength - 1); return true; } else { // we have to split the line in two parts it->SetLength(idx[0] - orgLineIndex[0]); IndexType newIdx = idx; ++newIdx[0]; LengthType newLength = orgLineLength - it->GetLength() - 1; m_LineContainer.push_back(LineType(newIdx, newLength)); return true; } } ++it; } return false; } /** * Add an index to the object. If the index is already in the object, the index can * be found several time in the object. */ template void LabelObject::AddIndex(const IndexType & idx) { if (!m_LineContainer.empty()) { // can we use the last line to add that index ? LineType & lastLine = *m_LineContainer.rbegin(); if (lastLine.IsNextIndex(idx)) { lastLine.SetLength(lastLine.GetLength() + 1); return; } } // create a new line this->AddLine(idx, 1); } /** * Add a new line to the object, without any check. */ template void LabelObject::AddLine(const IndexType & idx, const LengthType & length) { LineType line(idx, length); this->AddLine(line); } /** * Add a new line to the object, without any check. */ template void LabelObject::AddLine(const LineType & line) { m_LineContainer.push_back(line); } template auto LabelObject::GetNumberOfLines() const -> SizeValueType { return static_cast::SizeValueType>(m_LineContainer.size()); } template auto LabelObject::GetLine(SizeValueType i) const -> const LineType & { return m_LineContainer[i]; } template auto LabelObject::GetLine(SizeValueType i) -> LineType & { return m_LineContainer[i]; } template auto LabelObject::Size() const -> SizeValueType { int size = 0; for (auto it = m_LineContainer.begin(); it != m_LineContainer.end(); ++it) { size += it->GetLength(); } return size; } template bool LabelObject::Empty() const { return this->m_LineContainer.empty(); } template auto LabelObject::GetIndex(SizeValueType offset) const -> IndexType { SizeValueType o = offset; auto it = this->m_LineContainer.begin(); while (it != m_LineContainer.end()) { SizeValueType size = it->GetLength(); if (o >= size) { o -= size; } else { IndexType idx = it->GetIndex(); idx[0] += o; return idx; } ++it; } itkGenericExceptionMacro("Invalid offset: " << offset); } /** Copy the lines from another node. */ template template void LabelObject::CopyLinesFrom(const TSourceLabelObject * src) { itkAssertOrThrowMacro((src != nullptr), "Null Pointer"); // clear original lines and copy lines m_LineContainer.clear(); for (size_t i = 0; i < src->GetNumberOfLines(); ++i) { this->AddLine(src->GetLine(static_cast(i))); } this->Optimize(); } /** Copy the attributes of another node to this one */ template template void LabelObject::CopyAttributesFrom(const TSourceLabelObject * src) { itkAssertOrThrowMacro((src != nullptr), "Null Pointer"); m_Label = src->GetLabel(); } /** Copy the lines, the label and the attributes from another node. */ template template void LabelObject::CopyAllFrom(const TSourceLabelObject * src) { itkAssertOrThrowMacro((src != nullptr), "Null Pointer"); // Basically all derived class just need to copy the following two // lines to copy all data members this->template CopyLinesFrom(src); this->template CopyAttributesFrom(src); } /** Reorder the lines, merge the touching lines and ensure that no * pixel is covered by two lines */ template void LabelObject::Optimize() { if (!m_LineContainer.empty()) { // first copy the lines in another container and clear the current one LineContainerType lineContainer = m_LineContainer; m_LineContainer.clear(); // reorder the lines typename Functor::LabelObjectLineComparator comparator; std::sort(lineContainer.begin(), lineContainer.end(), comparator); // then check the lines consistency // we'll proceed line index by line index IndexType currentIdx = lineContainer.begin()->GetIndex(); LengthType currentLength = lineContainer.begin()->GetLength(); typename LineContainerType::const_iterator it = lineContainer.begin(); while (it != lineContainer.end()) { const LineType & line = *it; IndexType idx = line.GetIndex(); LengthType length = line.GetLength(); // check the index to be sure that we are still in the same line idx bool sameIdx = true; for (unsigned int i = 1; i < ImageDimension; ++i) { if (currentIdx[i] != idx[i]) { sameIdx = false; } } // try to extend the current line idx, or create a new line if (sameIdx && currentIdx[0] + (OffsetValueType)currentLength >= idx[0]) { // we may expand the line LengthType newLength = idx[0] + (OffsetValueType)length - currentIdx[0]; currentLength = std::max(newLength, currentLength); } else { // add the previous line to the new line container and use the new line // index and size this->AddLine(currentIdx, currentLength); currentIdx = idx; currentLength = length; } ++it; } // complete the last line this->AddLine(currentIdx, currentLength); } } template void LabelObject::Shift(OffsetType offset) { for (auto it = m_LineContainer.begin(); it != m_LineContainer.end(); ++it) { LineType & line = *it; line.SetIndex(line.GetIndex() + offset); } } template void LabelObject::Clear() { m_LineContainer.clear(); } template void LabelObject::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "LineContainer: " << &m_LineContainer << std::endl; os << indent << "Label: " << static_cast::PrintType>(m_Label) << std::endl; } } // end namespace itk #endif