![]() |
| 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