OpenCV系列笔记九:Comparing colors using the Strategy design pattern

Overview

介绍通过比较颜色来检测一片区域,并进行处理的方法,采用了Strategy design pattern.

Let’s say we want to build a simple algorithm that will identify all of the
pixels in an image that have a given color. For this, the algorithm has to
accept an image and a color as input and will return a binary image
showing the pixels that have the specified color. The tolerance with
which we want to accept a color will be another parameter to be
specified before running the algorithm.

Code

代码通过两种方式来进行颜色的比较, 一种是自己实现的,另一种是应用了OpenCV提供的函数absdiff等。最后将这种算法的效果和floodfill进行比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>


//Comparing colors using the Strategy design pattern

class ColorDetector{
public:
// minimun accptable distance
int maxDist;
// target color
cv::Vec3b target;

// image containing resulting binary map
cv::Mat result;

// empty constructor
// default parameter initialization here
ColorDetector():maxDist(50),target(0,0,0){};

// full constructor with target and distance
// functor
ColorDetector(uchar blue, uchar green, uchar red, int maxDist=50):
maxDist(maxDist){
setTargetColor(blue, green, red);
}

// functor--> ()
cv::Mat operator()(const cv::Mat &image){
return process_bycv(image);
}


// Sets the color distance threshold
// Threshold must be positive,
// otherwise distance threshold is set to 0.
void setColorDistanceThreshold(int distance){
if(distance<0)
distance = 0;
maxDist = distance;
}

// Gets the color distance threshold
int getColorDistanceThreshold() const{
return maxDist;
}


// Sets the color to be detected
void setTargetColor(uchar blue, uchar green, uchar red){
// BGR order
target = cv::Vec3b(blue, green, red);
}

// Sets the color to be detected
void setTargetColor(cv::Vec3b color){
target = color;
}

// Gets the color to be detected
cv::Vec3b getTargetColor() const{
return target;
}

// process the image
cv::Mat process(const cv::Mat &image);
cv::Mat process_bycv(const cv::Mat &image);


int getColorDitanceToTargetColor(const cv::Vec3b& color) const {
return getColorDistance(color, target);
}
// Compute the city-block distance between two colors
int getColorDistance(const cv::Vec3b&color1, const cv::Vec3b& color2) const {
return abs(color1[0]-color2[0]+
abs(color1[1])-color2[1]+
abs(color1[2]-color2[2]));
}

};


cv::Mat ColorDetector::process(const cv::Mat &image){
// re-allocate binary map if necessary
// same size as input image, but 1-channel
result.create(image.size(), CV_8U);

// processing
// get the iterators
cv::Mat_<cv::Vec3b>::const_iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::const_iterator itend = image.end<cv::Vec3b>();
cv::Mat_<uchar>::iterator itout = result.begin<uchar>();

// for each pixel
for(; it!=itend;++it, ++itout){
// compute distance from target color
if(getColorDitanceToTargetColor(*it) <= maxDist){
*itout = 255;
}else{
*itout = 0;
}
}
return result;
}

cv::Mat ColorDetector::process_bycv(const cv::Mat &image) {
cv::Mat output;
// compute abssolute difference with tarfet color
cv::absdiff(image, cv::Scalar(target) ,output);

//split the channels into 3 images;
std::vector<cv::Mat> images;
cv::split(output, images);

// add the 3 channels (saturation might occurs here)
output = images[0] + images[1] + images[2];
// apply threshold
cv::threshold(output, output, maxDist, 255, cv::THRESH_BINARY_INV);
return output;
}


int main(){
//1. Create image processor object
ColorDetector cdetect;
//2. Read input image
cv::Mat image = cv::imread("/home/shensir/Documents/MyPrograming/Cpp/Clions/data/lake.png");
if(image.empty()) return 0;
else cv::imshow("Original Image", image);

//3. Set input parameters
cdetect.setTargetColor(230, 190, 130);

//4. Process the image and display the result
cv::Mat result1 = cdetect.process(image);
cv::imshow("process", result1);

cv::Mat result2 = cdetect.process_bycv(image);
cv::imshow("process_bycv", result2);



// another way
ColorDetector colordetector(230, 190, 130);
cv::Mat result = colordetector(image);
cv::imshow("colordetector result", result);

// show the result in original image
cv::Mat image_masked, mask_inv;
mask_inv = 255 - result;
cv::bitwise_and(image, image, image_masked, mask_inv);
cv::add(image_masked, cv::Scalar(255, 255, 255), image_masked, result);
cv::imshow("image_masked", image_masked);


//floodFill的用法

cv::floodFill(image, cv::Point(100,50),cv::Scalar(255,255,255),
(cv::Rect*)0, cv::Scalar(35,35,35), cv::Scalar(35,35,35),
cv::FLOODFILL_FIXED_RANGE);
cv::imshow("Flood fill", image);

cv::waitKey(0);
return 0;

}


输出:

关于floodfill和原算法的不同之处:

Our ColorDetector class identifies the pixels in an image that have a
color similar to a given target color. The decision to accept or not a pixel
is simply made on a per-pixel basis. The cv::floodFill function
proceeds in a very similar way with one important difference: in this
case, the decision to accept a pixel also depends on the state of its
neighbors. The idea is to identify a connected area of a certain color.
The user specifies a starting pixel location and tolerance parameters that
determine color similarity.

本文标题:OpenCV系列笔记九:Comparing colors using the Strategy design pattern

文章作者:不秩稚童

发布时间:2017年04月28日 - 13:20:46

最后更新:2017年04月28日 - 13:30:59

原始链接:http://datahonor.com/2017/04/28/OpenCV系列笔记九:Comparing-colors-using-the-Strategy-design-pattern/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

击蒙御寇