Я дам вам свой ответ в C++
, но те же операции должны быть доступны в Emgu CV
.
Я предлагаю следующий подход: Сегментируйте (то есть - отдельно) целевой объект, используя цветовое пространство HSV . Вычислите двоичную маску для интересующего объекта. Получите самый большой blob в двоичной маске, это должна быть карта. Вычислите ограничивающую рамку карты. Обрезать карту из входного изображения
Хорошо, сначала получите (или прочтите) входное изображение. Примените фильтр median blur
, он поможет избавиться от высокочастотного шума (маленькие серые капли), который вы видите на входе. Основным параметром для настройки является size
от kernel
(или апертуры фильтра), однако будьте осторожны - высокое значение приведет к агрессивному эффекту и, вероятно, разрушит ваше изображение:
//read input image:
std::string imageName = "C://opencvImages//yoshiButNotYoshi.png";
cv::Mat imageInput = cv::imread( imageName );
//apply a median blur filter, the size of the kernel is 5 x 5:
cv::Mat blurredImage;
cv::medianBlur ( imageInput, blurredImage, 5 );
Это результат фильтра размытия (размер встроенного изображения изменен) :
Затем сегментируйте изображение. Воспользуйтесь тем, что фон белый, а все остальное (в основном интересующий объект) имеет некоторую информацию о цвете. Вы можете использовать цветовое пространство HSV
. Сначала преобразуйте изображение BGR
в HSV
:
//BGR to HSV conversion:
cv::Mat hsvImg;
cv::cvtColor( blurredImage, hsvImg, CV_RGB2HSV );
Цветовое пространство HSV
кодирует информацию о цвете иначе, чем типичное цветовое пространство BGR/RGB
. Его преимущество перед другими цветовыми моделями в значительной степени зависит от приложения, но в целом он более надежен при работе с градиентами оттенка . Я попытаюсь получить двоичную маску на основе HSV для интересующего объекта.
В двоичной маске все, что вас интересует на входном изображении, окрашено в white
, все остальное в black
(или наоборот). Вы можете получить эту маску с помощью функции inRange
. Однако необходимо указать диапазоны цветов, которые будут отображаться в белом (или черном) цвете в выходной маске. Для вашего изображения и с использованием цветовой модели HSV эти значения:
cv::Scalar minColor( 0, 0, 100 ); //the lower range of colors
cv::Scalar maxColor( 0, 0, 255 ); //the upper range of colors
Теперь получите двоичную маску:
//prepare the binary mask:
cv::Mat binaryMask;
//create the binary mask using the specified range of color
cv::inRange( hsvImg, minColor, maxColor, binaryMask );
//invert the mask:
binaryMask = 255 - binaryMask;
Вы получите это изображение:
Теперь вы можете избавиться от некоторого шума (который пережил фильтр размытия) с помощью morphological filtering
. Морфологические фильтры - это, по сути, логические правила, применяемые к двоичным (или серым) изображениям. Они берут на входе «соседство» пикселей и применяют логические функции для получения вывода. Они очень удобны при очистке двоичных изображений. Для этого я применю серию логических фильтров.
Сначала я erode
изображение, а затем dilate
, используя 3 iterations
. structuring element
- это rectangle
размера 3 x 3
:
//apply some morphology the clean the binary mask a little bit:
cv::Mat SE = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3, 3) );
int morphIterations = 3;
cv::morphologyEx( binaryMask, binaryMask, cv::MORPH_ERODE, SE, cv::Point(-1,-1), morphIterations );
cv::morphologyEx( binaryMask, binaryMask, cv::MORPH_DILATE, SE, cv::Point(-1,-1), morphIterations );
Вы получаете этот результат. Посмотрите, как в основном исчезли шумные капли:
А теперь самое интересное. Вы можете пропустить oop через все contours
на этом изображении и получить самый большой из них всех. Это типичная операция, которую я выполняю постоянно, поэтому я написал функцию, которая ее выполняет. Он называется findBiggestBlob
. Я представлю функцию позже. Проверьте результат, который вы получите после поиска и извлечения самого большого двоичного объекта:
//find the biggest blob in the binary image:
cv::Mat biggestBlob = findBiggestBlob( binaryMask );
Вы получите это:
Теперь вы можете получить bounding box
самого большого двоичного объекта, используя boundingRect
:
//Get the bounding box of the biggest blob:
cv::Rect bBox = cv::boundingRect( biggestBlob );
Давайте нарисуем bounding box
на входном изображении:
cv::Mat imageClone = imageInput.clone();
cv::rectangle( imageClone, bBox, cv::Scalar(255,0,0), 2 );
Наконец, давайте вырежем карточку из входного изображения:
cv::Mat croppedImage = imageInput( bBox );
Это обрезанный результат:
Это код для функции findBiggestBlob
. Идея состоит в том, чтобы просто вычислить все контуры на двоичном входе, вычислить их площадь и сохранить контур с наибольшей площадью пучка:
//Function to get the largest blob in a binary image:
cv::Mat findBiggestBlob( cv::Mat &inputImage ){
cv::Mat biggestBlob = inputImage.clone();
int largest_area = 0;
int largest_contour_index = 0;
std::vector< std::vector<cv::Point> > contours; // Vector for storing contour
std::vector< cv::Vec4i > hierarchy;
// Find the contours in the image
cv::findContours( biggestBlob, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
for( int i = 0; i < (int)contours.size(); i++ ) {
//Find the area of the contour
double a = cv::contourArea( contours[i], false);
//Store the index of largest contour:
if( a > largest_area ){
largest_area = a;
largest_contour_index = i;
}
}
//Once you get the biggest blob, paint it black:
cv::Mat tempMat = biggestBlob.clone();
cv::drawContours( tempMat, contours, largest_contour_index, cv::Scalar(0),
CV_FILLED, 8, hierarchy );
//Erase the smaller blobs:
biggestBlob = biggestBlob - tempMat;
tempMat.release();
return biggestBlob;
}