15#include "mitkPointSet.h"
17#include <opencv2/imgproc.hpp>
18#include <opencv2/imgproc/imgproc_c.h>
24#define GMM_COMPONENTS_COUNT 5
27 : m_ModelPointsDilationSize(0),
28 m_UseOnlyRegionAroundModelPoints(false),
29 m_CurrentProcessImageNum(0),
34 m_Thread = std::thread(&GrabCutOpenCVImageFilter::SegmentationWorker,
this);
41 m_WorkerBarrier.notify_all();
42 if (m_Thread.joinable())
50 MITK_WARN <<
"Filtering empty image?";
56 if (image.type() != CV_8UC3)
58 cv::Mat tmp = image.clone();
59 cv::cvtColor(tmp, image, CV_GRAY2RGB);
65 m_InputImage = image.clone();
66 m_InputImageId = this->GetCurrentImageId();
67 m_ImageMutex.unlock();
71 if ( ! m_ForegroundPoints.empty()) { m_WorkerBarrier.notify_all(); }
78 m_PointSetsMutex.lock();
79 m_ForegroundPoints = foregroundPoints;
80 m_PointSetsMutex.unlock();
85 m_PointSetsMutex.lock();
86 m_BackgroundPoints = backgroundPoints;
87 m_ForegroundPoints = foregroundPoints;
88 m_PointSetsMutex.unlock();
93 m_PointSetsMutex.lock();
94 m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask);
95 m_PointSetsMutex.unlock();
100 m_PointSetsMutex.lock();
101 m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask);
102 m_BackgroundPoints = this->ConvertMaskToModelPointsList(backgroundMask);
103 m_PointSetsMutex.unlock();
108 if ( modelPointsDilationSize < 0 )
110 MITK_ERROR(
"AbstractOpenCVImageFilter")(
"GrabCutOpenCVImageFilter")
111 <<
"Model points dilation size must not be smaller then zero.";
112 mitkThrow() <<
"Model points dilation size must not be smaller then zero.";
115 m_ModelPointsDilationSize = modelPointsDilationSize;
120 m_UseOnlyRegionAroundModelPoints =
true;
121 m_AdditionalWidth = additionalWidth;
126 m_UseOnlyRegionAroundModelPoints =
false;
131 return m_BoundingBox;
136 return m_ResultImageId;
143 m_ResultMutex.lock();
144 result = m_ResultMask.clone();
145 m_ResultMutex.unlock();
152 std::vector<std::vector<cv::Point> > cvContours;
153 std::vector<cv::Vec4i> hierarchy;
154 std::vector<mitk::GrabCutOpenCVImageFilter::ModelPointsList> contourPoints;
156 cv::Mat resultMask = this->GetResultMask();
157 if (resultMask.empty()) {
return contourPoints; }
159 cv::findContours(resultMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
162 for (
unsigned int i = 0; i < cvContours.size(); ++i )
166 for (
auto it = cvContours[i].begin();
167 it != cvContours[i].end(); ++it)
170 index.SetElement(0, it->x);
171 index.SetElement(1, it->y);
172 curContourPoints.push_back(index);
175 contourPoints.push_back(curContourPoints);
178 return contourPoints;
183 cv::Mat mask = this->GetResultMask();
187 if (pixelIndex.GetElement(0) < 0 || pixelIndex.GetElement(0) >= mask.size().height
188 || pixelIndex.GetElement(1) < 0 || pixelIndex.GetElement(1) >= mask.size().width)
190 MITK_WARN(
"AbstractOpenCVImageFilter")(
"GrabCutOpenCVImageFilter")
191 <<
"Given pixel index ("<< pixelIndex.GetElement(0) <<
", " << pixelIndex.GetElement(1)
192 <<
") is outside the image (" << mask.size().height <<
", " << mask.size().width <<
").";
199 cv::floodFill(mask, cv::Point(pixelIndex.GetElement(0), pixelIndex.GetElement(1)), 5);
201 cv::Mat foregroundMask;
202 cv::compare(mask, 5, foregroundMask, cv::CMP_EQ);
205 std::vector<std::vector<cv::Point> > cvContours;
206 std::vector<cv::Vec4i> hierarchy;
207 cv::findContours(foregroundMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
212 for (
auto it = cvContours[0].begin();
213 it != cvContours[0].end(); ++it)
216 index.SetElement(0, it->x);
217 index.SetElement(1, it->y);
218 contourPoints.push_back(index);
221 return contourPoints;
227 cv::Mat mask(m_InputImage.size().height, m_InputImage.size().width, CV_8UC1, cv::GC_PR_BGD);
230 m_PointSetsMutex.lock();
232 m_PointSetsMutex.unlock();
235 unsigned int pixelValues[2] = {cv::GC_FGD, cv::GC_BGD};
237 for (
unsigned int n = 0; n < 2; ++n)
239 for (
auto it = pointsLists[n].begin();
240 it != pointsLists[n].end(); ++it)
244 for (
int i = -m_ModelPointsDilationSize; i <= m_ModelPointsDilationSize; ++i )
246 for (
int j = -m_ModelPointsDilationSize; j <= m_ModelPointsDilationSize; ++j)
248 int x = it->GetElement(1) + i;
int y = it->GetElement(0) + j;
249 if ( x >= 0 && y >= 0 && x < mask.cols && y < mask.rows)
251 mask.at<
unsigned char>(x, y) = pixelValues[n];
263 cv::Mat nonPropablyBackgroundMask, modelPoints;
264 cv::compare(mask, cv::GC_PR_BGD, nonPropablyBackgroundMask, cv::CMP_NE);
265 cv::findNonZero(nonPropablyBackgroundMask, modelPoints);
267 if (modelPoints.empty())
269 MITK_WARN(
"AbstractOpenCVImageFilter")(
"GrabCutOpenCVImageFilter")
270 <<
"Cannot find any foreground points. Returning full image size as bounding rectangle.";
271 return cv::Rect(0, 0, mask.rows, mask.cols);
275 cv::Rect boundingRect = cv::boundingRect(modelPoints);
278 boundingRect.x =
static_cast<unsigned int>(boundingRect.x) > m_AdditionalWidth ? boundingRect.x - m_AdditionalWidth : 0;
279 boundingRect.y =
static_cast<unsigned int>(boundingRect.y) > m_AdditionalWidth ? boundingRect.y - m_AdditionalWidth : 0;
283 if (
static_cast<unsigned int>(boundingRect.x + boundingRect.width)
284 + 2 * m_AdditionalWidth <
static_cast<unsigned int>(mask.size().width) )
286 boundingRect.width += 2 * m_AdditionalWidth;
290 boundingRect.width = mask.size().width - boundingRect.x - 1;
295 if (
static_cast<unsigned int>(boundingRect.y + boundingRect.height)
296 + 2 * m_AdditionalWidth <
static_cast<unsigned int>(mask.size().height) )
298 boundingRect.height += 2 * m_AdditionalWidth;
302 boundingRect.height = mask.size().height - boundingRect.y - 1;
305 assert(boundingRect.x + boundingRect.width < mask.size().width);
306 assert(boundingRect.y + boundingRect.height < mask.size().height);
314 cv::Mat compareFgResult, compareBgResult;
315 cv::compare(mask, cv::GC_FGD, compareFgResult, cv::CMP_EQ);
316 cv::compare(mask, cv::GC_PR_BGD, compareBgResult, cv::CMP_EQ);
321 return cv::Mat::zeros(mask.size(), mask.type());
325 cv::Mat bgdModel, fgdModel;
326 cv::grabCut(input, mask, cv::Rect(), bgdModel, fgdModel, 1, cv::GC_INIT_WITH_MASK);
330 cv::compare(mask, cv::GC_PR_FGD, result, cv::CMP_EQ);
333 cv::Mat foregroundMat;
334 cv::compare(mask, cv::GC_FGD, foregroundMat, cv::CMP_EQ);
335 foregroundMat.copyTo(result, foregroundMat);
343 cv::findNonZero(mask, points);
347 for (
size_t n = 0; n < points.total(); ++n)
350 index.SetElement(0, points.at<cv::Point>(n).x);
351 index.SetElement(1, points.at<cv::Point>(n).y);
352 pointsVector.push_back(index);
358void mitk::GrabCutOpenCVImageFilter::SegmentationWorker()
361 std::unique_lock<std::mutex> lock(mutex);
365 m_WorkerBarrier.wait(lock, [
this] {
return !m_StopThread; });
368 cv::Mat image = m_InputImage.clone();
369 int inputImageId = m_InputImageId;
370 m_ImageMutex.unlock();
372 cv::Mat mask = this->GetMaskFromPointSets();
375 if (m_UseOnlyRegionAroundModelPoints)
377 result = cv::Mat(mask.rows, mask.cols, mask.type(), 0.0);
378 m_BoundingBox = this->GetBoundingRectFromMask(mask);
379 RunSegmentation(image(m_BoundingBox), mask(m_BoundingBox)).copyTo(result(m_BoundingBox));
383 result = this->RunSegmentation(image, mask);
387 m_ResultMutex.lock();
388 m_ResultMask = result;
389 m_ResultImageId = inputImageId;
390 m_ResultMutex.unlock();
Interface for image filters on OpenCV images.
~GrabCutOpenCVImageFilter() override
bool OnFilterImage(cv::Mat &image) override
Implementation of the virtual image filtering method. The input image is copied to a member attribute...
void SetUseFullImage()
The full image is used as input for the segmentation. This method sets the behaviour back to the defa...
std::vector< ModelPointsList > GetResultContours()
Getter for the contours of the current segmentation.
void SetModelPointsDilationSize(int modelPointsDilationSize)
Set a size of which each model point is dilated before image filtering. The more color information of...
std::vector< itk::Index< 2 > > ModelPointsList
List holding image indices of the model points.
cv::Rect GetBoundingRectFromMask(cv::Mat mask)
Creates a bounding box around all pixels which aren't propably background. The bounding box is widene...
cv::Mat RunSegmentation(cv::Mat input, cv::Mat mask)
Performs a GrabCut segmentation of the given input image.
cv::Rect GetRegionAroundModelPoints()
Getter for the rectangle used for the area of segmentation. See mitk::GrabCutOpenCVImageFilter::SetUs...
ModelPointsList GetResultContourWithPixel(itk::Index< 2 > pixelIndex)
Getter for one specific contour of the current segmentation.
int GetResultImageId()
Getter for an ascending id of the current result image. The id will be increased for every segmentati...
void SetModelPoints(ModelPointsList foregroundPoints)
Sets a list of image indices as foreground model points.
cv::Mat GetResultMask()
Getter for the result mask of the current segmentation. The result of this method is not necessarily ...
cv::Mat GetMaskFromPointSets()
Creates an image mask for GrabCut algorithm by using the foreground and background point sets....
void SetUseOnlyRegionAroundModelPoints(unsigned int additionalBorder)
Use only the region around the foreground model points for the segmentation.
ModelPointsList ConvertMaskToModelPointsList(cv::Mat mask)
Creates a list of points from every non-zero pixel of the given mask.
GrabCutOpenCVImageFilter()
#define GMM_COMPONENTS_COUNT