graph_00 |
Otsu
void separate_by_otsu(cv::Mat const &src) { cv::Mat result = src.clone(); cv::threshold(result, result, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU); }
graph_01(process by otsu) |
Adaptive Threshold
void separate_adaptive_threshold(cv::Mat const &src) { cv::Mat result = src.clone(); cv::adaptiveThreshold(result, result, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, 7); cv::adaptiveThreshold(result, result, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 7, 7); }
graph_02(process by adaptive gaussian) |
graph_03(process by adaptive mean) |
Kmeans
As you can see, the results(graph_01~graph_03) can't cut out the second card.Luckily, the colors of the cards and the background are different, a fairly good example for kmeans.
To adopt the cv::kmeans, we need to transfer the image into a samples, each data set of the samples should consist a pixel groups(ex : bgr, rgb, hsv and so on).To generate the image after color segmentation, we have to map the centers generated by the cv::kmeans to the image.
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> int main() { cv::Mat src = cv::imread(Folder + "perspective05.jpg"); if(src.empty()){ std::cerr<"can't read the image"<std::endl; return -1; } //step 1 : map the src to the samples cv::Mat samples(src.total(), 3, CV_32F); auto samples_ptr = samples.ptr<float>(0); for( int row = 0; row != src.rows; ++row){ auto src_begin = src.ptr<uchar>(row); auto src_end = src_begin + src.cols * src.channels(); //auto samples_ptr = samples.ptr<float>(row * src.cols); while(src_begin != src_end){ samples_ptr[0] = src_begin[0]; samples_ptr[1] = src_begin[1]; samples_ptr[2] = src_begin[2]; samples_ptr += 3; src_begin +=3; } } //step 2 : apply kmeans to find labels and centers int clusterCount = 3; cv::Mat labels; int attempts = 5; cv::Mat centers; cv::kmeans(samples, clusterCount, labels, cv::TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10, 0.01), attempts, cv::KMEANS_PP_CENTERS, centers); //step 3 : map the centers to the output cv::Mat new_image(src.size(), src.type()); for( int row = 0; row != src.rows; ++row){ auto new_image_begin = new_image.ptr<uchar>(row); auto new_image_end = new_image_begin + new_image.cols * 3; auto labels_ptr = labels.ptr<int>(row * src.cols); while(new_image_begin != new_image_end){ int const cluster_idx = *labels_ptr; auto centers_ptr = centers.ptr<float>(cluster_idx); new_image_begin[0] = centers_ptr[0]; new_image_begin[1] = centers_ptr[1]; new_image_begin[2] = centers_ptr[2]; new_image_begin += 3; ++labels_ptr; } } cv::Mat binary; cv::Canny(new_image, binary, 30, 90); cv::imshow("original", src); cv::imshow("binary", binary); cv::imshow( "clustered image", new_image ); cv::waitKey(); return 0; }
In step 1, we rearrange src to sample as following
src :
(b00,g00,r00) (b01,g01,r01) (b02,g02,r02).......
(b10,g10,r10) (b11,g11,r11) (b12,g12,r12).......
............
to
sample :
(b00,g00,r00)
(b01,g01,r01)
(b02,g02,r02)
...........
(b10,g10,r10)
(b11,g11,r11)
(b12,g12,r12)
................
The results of the labels are associated with the samples generated by cv::kmeans.The order of reading the labels should be the same as you generated the samples.
graph_04(process by kmeans) |
graph_05(apply canny on graph_04) |
Now we could cut the cards from graph_04 without much troubles.To simplify the procedures of kmeans segmentation, I encapsulate it by class.The codes can download from github.
No comments:
Post a Comment