22 #include "itkMultiThreader.h"
23 #include "itkFastMutexLock.h"
24 #include "itkConditionVariable.h"
30 #define GMM_COMPONENTS_COUNT 5
33 : m_ModelPointsDilationSize(0),
34 m_UseOnlyRegionAroundModelPoints(false),
35 m_CurrentProcessImageNum(0),
38 m_ThreadId(-1), m_StopThread(false),
39 m_MultiThreader(
itk::MultiThreader::
New()),
40 m_WorkerBarrier(
itk::ConditionVariable::
New()),
41 m_ImageMutex(
itk::FastMutexLock::
New()),
42 m_ResultMutex(
itk::FastMutexLock::
New()),
43 m_PointSetsMutex(
itk::FastMutexLock::
New())
45 m_ThreadId = m_MultiThreader->SpawnThread(this->SegmentationWorker,
this);
52 m_WorkerBarrier->Broadcast();
53 if ( m_ThreadId >= 0) { m_MultiThreader->TerminateThread(m_ThreadId); }
66 if (image.type() != CV_8UC3)
68 cv::Mat tmp = image.clone();
69 cv::cvtColor(tmp, image, CV_GRAY2RGB);
75 m_InputImage = image.clone();
76 m_InputImageId = this->GetCurrentImageId();
77 m_ImageMutex->Unlock();
81 if ( ! m_ForegroundPoints.empty()) { m_WorkerBarrier->Broadcast(); }
88 m_PointSetsMutex->Lock();
89 m_ForegroundPoints = foregroundPoints;
90 m_PointSetsMutex->Unlock();
95 m_PointSetsMutex->Lock();
96 m_BackgroundPoints = backgroundPoints;
97 m_ForegroundPoints = foregroundPoints;
98 m_PointSetsMutex->Unlock();
103 m_PointSetsMutex->Lock();
104 m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask);
105 m_PointSetsMutex->Unlock();
110 m_PointSetsMutex->Lock();
111 m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask);
112 m_BackgroundPoints = this->ConvertMaskToModelPointsList(backgroundMask);
113 m_PointSetsMutex->Unlock();
118 if ( modelPointsDilationSize < 0 )
120 MITK_ERROR(
"AbstractOpenCVImageFilter")(
"GrabCutOpenCVImageFilter")
121 <<
"Model points dilation size must not be smaller then zero.";
122 mitkThrow() <<
"Model points dilation size must not be smaller then zero.";
125 m_ModelPointsDilationSize = modelPointsDilationSize;
130 m_UseOnlyRegionAroundModelPoints =
true;
131 m_AdditionalWidth = additionalWidth;
136 m_UseOnlyRegionAroundModelPoints =
false;
141 return m_BoundingBox;
146 return m_ResultImageId;
153 m_ResultMutex->Lock();
154 result = m_ResultMask.clone();
155 m_ResultMutex->Unlock();
162 std::vector<std::vector<cv::Point> > cvContours;
163 std::vector<cv::Vec4i> hierarchy;
164 std::vector<mitk::GrabCutOpenCVImageFilter::ModelPointsList> contourPoints;
166 cv::Mat resultMask = this->GetResultMask();
167 if (resultMask.empty()) {
return contourPoints; }
169 cv::findContours(resultMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
172 for (
unsigned int i = 0; i < cvContours.size(); ++i )
176 for (
auto it = cvContours[i].begin();
177 it != cvContours[i].end(); ++it)
180 index.SetElement(0, it->x);
181 index.SetElement(1, it->y);
182 curContourPoints.push_back(index);
185 contourPoints.push_back(curContourPoints);
188 return contourPoints;
193 cv::Mat mask = this->GetResultMask();
197 if (pixelIndex.GetElement(0) < 0 || pixelIndex.GetElement(0) >= mask.size().height
198 || pixelIndex.GetElement(1) < 0 || pixelIndex.GetElement(1) >= mask.size().width)
200 MITK_WARN(
"AbstractOpenCVImageFilter")(
"GrabCutOpenCVImageFilter")
201 <<
"Given pixel index ("<< pixelIndex.GetElement(0) <<
", " << pixelIndex.GetElement(1)
202 <<
") is outside the image (" << mask.size().height <<
", " << mask.size().width <<
").";
209 cv::floodFill(mask, cv::Point(pixelIndex.GetElement(0), pixelIndex.GetElement(1)), 5);
211 cv::Mat foregroundMask;
215 std::vector<std::vector<cv::Point> > cvContours;
216 std::vector<cv::Vec4i> hierarchy;
217 cv::findContours(foregroundMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
222 for (
auto it = cvContours[0].begin();
223 it != cvContours[0].end(); ++it)
226 index.SetElement(0, it->x);
227 index.SetElement(1, it->y);
228 contourPoints.push_back(index);
231 return contourPoints;
237 cv::Mat mask(m_InputImage.size().height, m_InputImage.size().width, CV_8UC1, cv::GC_PR_BGD);
240 m_PointSetsMutex->Lock();
242 m_PointSetsMutex->Unlock();
245 unsigned int pixelValues[2] = {cv::GC_FGD, cv::GC_BGD};
247 for (
unsigned int n = 0; n < 2; ++n)
249 for (
auto it = pointsLists[n].begin();
250 it != pointsLists[n].end(); ++it)
254 for (
int i = -m_ModelPointsDilationSize; i <= m_ModelPointsDilationSize; ++i )
256 for (
int j = -m_ModelPointsDilationSize; j <= m_ModelPointsDilationSize; ++j)
258 int x = it->GetElement(1) + i;
int y = it->GetElement(0) + j;
259 if ( x >= 0 && y >= 0 && x < mask.cols && y < mask.rows)
261 mask.at<
unsigned char>(x, y) = pixelValues[n];
273 cv::Mat nonPropablyBackgroundMask, modelPoints;
274 cv::compare(mask, cv::GC_PR_BGD, nonPropablyBackgroundMask, cv::CMP_NE);
275 cv::findNonZero(nonPropablyBackgroundMask, modelPoints);
277 if (modelPoints.empty())
279 MITK_WARN(
"AbstractOpenCVImageFilter")(
"GrabCutOpenCVImageFilter")
280 <<
"Cannot find any foreground points. Returning full image size as bounding rectangle.";
281 return cv::Rect(0, 0, mask.rows, mask.cols);
285 cv::Rect boundingRect = cv::boundingRect(modelPoints);
288 boundingRect.x =
static_cast<unsigned int>(boundingRect.x) > m_AdditionalWidth ? boundingRect.x - m_AdditionalWidth : 0;
289 boundingRect.y = static_cast<unsigned int>(boundingRect.y) > m_AdditionalWidth ? boundingRect.y - m_AdditionalWidth : 0;
293 if ( static_cast<unsigned int>(boundingRect.x + boundingRect.width)
294 + 2 * m_AdditionalWidth < static_cast<unsigned int>(mask.size().width) )
296 boundingRect.width += 2 * m_AdditionalWidth;
300 boundingRect.width = mask.size().width - boundingRect.x - 1;
305 if ( static_cast<unsigned int>(boundingRect.y + boundingRect.height)
306 + 2 * m_AdditionalWidth < static_cast<unsigned int>(mask.size().height) )
308 boundingRect.height += 2 * m_AdditionalWidth;
312 boundingRect.height = mask.size().height - boundingRect.y - 1;
315 assert(boundingRect.x + boundingRect.width < mask.size().width);
316 assert(boundingRect.y + boundingRect.height < mask.size().height);
324 cv::Mat compareFgResult, compareBgResult;
325 cv::compare(mask, cv::GC_FGD, compareFgResult, cv::CMP_EQ);
326 cv::compare(mask, cv::GC_PR_BGD, compareBgResult, cv::CMP_EQ);
331 return cv::Mat::zeros(mask.size(), mask.type());
335 cv::Mat bgdModel, fgdModel;
336 cv::grabCut(input, mask, cv::Rect(), bgdModel, fgdModel, 1, cv::GC_INIT_WITH_MASK);
340 cv::compare(mask, cv::GC_PR_FGD, result, cv::CMP_EQ);
343 cv::Mat foregroundMat;
344 cv::compare(mask, cv::GC_FGD, foregroundMat, cv::CMP_EQ);
345 foregroundMat.copyTo(result, foregroundMat);
353 cv::findNonZero(mask, points);
357 for (
size_t n = 0; n < points.total(); ++n)
360 index.SetElement(0, points.at<cv::Point>(n).x);
361 index.SetElement(1, points.at<cv::Point>(n).y);
362 pointsVector.push_back(index);
368 ITK_THREAD_RETURN_TYPE mitk::GrabCutOpenCVImageFilter::SegmentationWorker(
void* pInfoStruct)
371 struct itk::MultiThreader::ThreadInfoStruct * pInfo = (
struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct;
374 itk::SimpleMutexLock mutex;
379 if (thisObject->m_StopThread) {
break; }
381 thisObject->m_WorkerBarrier->Wait(&mutex);
383 if (thisObject->m_StopThread) {
break; }
385 thisObject->m_ImageMutex->Lock();
388 thisObject->m_ImageMutex->Unlock();
395 result = cv::Mat(mask.rows, mask.cols, mask.type(), 0.0);
405 thisObject->m_ResultMutex->Lock();
408 thisObject->m_ResultMutex->Unlock();
413 return ITK_THREAD_RETURN_VALUE;
std::vector< itk::Index< 2 > > ModelPointsList
List holding image indices of the model points.
cv::Mat RunSegmentation(cv::Mat input, cv::Mat mask)
Performs a GrabCut segmentation of the given input image.
ModelPointsList ConvertMaskToModelPointsList(cv::Mat mask)
Creates a list of points from every non-zero pixel of the given mask.
int m_ResultImageId
id of the image which segmentation result is currently present in m_ResultMask
std::vector< ModelPointsList > GetResultContours()
Getter for the contours of the current segmentation.
cv::Rect GetBoundingRectFromMask(cv::Mat mask)
Creates a bounding box around all pixels which aren't propably background. The bounding box is widene...
bool m_UseOnlyRegionAroundModelPoints
bool OnFilterImage(cv::Mat &image) override
Implementation of the virtual image filtering method. The input image is copied to a member attribute...
ModelPointsList GetResultContourWithPixel(itk::Index< 2 > pixelIndex)
Getter for one specific contour of the current segmentation.
void SetUseFullImage()
The full image is used as input for the segmentation. This method sets the behaviour back to the defa...
cv::Mat GetMaskFromPointSets()
Creates an image mask for GrabCut algorithm by using the foreground and background point sets...
GrabCutOpenCVImageFilter()
void SetModelPoints(ModelPointsList foregroundPoints)
Sets a list of image indices as foreground model points.
Makes the OpenCV GrabCut filter available as OpenCVImageFilter.
bool compare(std::pair< double, int > i, std::pair< double, int > j)
#define GMM_COMPONENTS_COUNT
cv::Rect GetRegionAroundModelPoints()
Getter for the rectangle used for the area of segmentation. See mitk::GrabCutOpenCVImageFilter::SetUs...
void SetModelPointsDilationSize(int modelPointsDilationSize)
Set a size of which each model point is dilated before image filtering. The more color information of...
Interface for image filters on OpenCV images.
virtual ~GrabCutOpenCVImageFilter()
void SetUseOnlyRegionAroundModelPoints(unsigned int additionalBorder)
Use only the region around the foreground model points for the segmentation.
cv::Mat GetResultMask()
Getter for the result mask of the current segmentation. The result of this method is not necessarily ...
int m_InputImageId
id of the image currently set as m_InputImage
int GetResultImageId()
Getter for an ascending id of the current result image. The id will be increased for every segmentati...
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.