Direct Graphical Models  v.1.7.0
MarkerHistogram.cpp
1 #include "MarkerHistogram.h"
2 #include "DGM/TrainNodeNaiveBayes.h"
3 #include "DGM/IPDF.h"
4 #include "macroses.h"
5 
6 namespace DirectGraphicalModels { namespace vis
7 {
8 // Constants
9 const cv::Size CMarkerHistogram::margin = cv::Size(25, 16);
10 const byte CMarkerHistogram::bkgIntencity = 50;
11 const double CMarkerHistogram::frgWeight = 0.75;
12 const std::string CMarkerHistogram::wndName = "Feature Histogram Viewer";
13 
15 {
16  char str[256];
17  const word nFeatures = m_nodeTrainer.getNumFeatures();
18  const size_t n = m_vPalette.size();
19 
20  Mat res(2 * margin.height + 256, 2 * margin.height + 256, CV_8UC3);
21  res.setTo(bkgIntencity);
22  rectangle(res, Point(margin.height, margin.height), Point(256 + margin.height, 256 + margin.height), CV_RGB(0, 0, 0), -1);
23 
24  if (nFeatures == 2) {
25 #ifdef ENABLE_PPL
26  concurrency::parallel_for(0, 256, [&](int y) {
27 #else
28  for (int y = 0; y < 256; y++) {
29 #endif
30  Mat fv(2, 1, CV_8UC1);
31  fv.at<byte>(1, 0) = static_cast<byte>(y);
32  Vec3b *pRes = res.ptr<Vec3b>(margin.height + 255 - y);
33  for (int x = 0; x < 256; x++) {
34  fv.at<byte>(0, 0) = static_cast<byte>(x);
35  Mat pot = m_nodeTrainer.getNodePotentials(fv, 1.0f, Z);
36 
37  for (int s = 0; s < pot.rows; s++) {
38  float val = MIN(100, pot.at<float>(s, 0));
39  Scalar color = val * m_vPalette[s % n].first / 100;
40  pRes[margin.height + x] += Vec3b((byte)color[0], (byte)color[1], (byte)color[2]);
41  }
42  } // x
43  } // y
44 #ifdef ENABLE_PPL
45  );
46 #endif
47  }
48  else DGM_WARNING("The number of features (%d) is not 2", nFeatures);
49 
50  // Feature Names
51  Mat tmp(margin.height, res.rows, CV_8UC3);
52  tmp.setTo(bkgIntencity);
53  sprintf(str, " feature 1 255");
54  putText(tmp, str, Point(3, tmp.rows - 4), FONT_HERSHEY_SIMPLEX, 0.3, CV_RGB(255, 255, 255), 1, cv::LineTypes::LINE_AA);
55  flip(tmp.t(), tmp, 0);
56  tmp.copyTo(res(Rect(0, 0, margin.height, tmp.rows)));
57  sprintf(str, "0 feature 0 255");
58  putText(res, str, Point(3, res.rows - 6), FONT_HERSHEY_SIMPLEX, 0.3, CV_RGB(255, 255, 255), 1, cv::LineTypes::LINE_AA);
59 
60  // Figure box
61  rectangle(res, Point(margin.height - 1, margin.height - 1), Point(256 + margin.height, 256 + margin.height), CV_RGB(255, 255, 255));
62 
63  return res;
64 }
65 
67 {
68  static Mat histogramImg = drawHistogram(CV_RGB(0, 0, 0));
69  namedWindow(wndName.c_str(), WINDOW_AUTOSIZE);
70  imshow(wndName.c_str(), histogramImg);
71  setMouseCallback(wndName.c_str(), [](int Event, int x, int y, int flags, void *param) {
72  if (Event != cv::MouseEventFlags::EVENT_FLAG_LBUTTON) return;
73  CMarkerHistogram *pUserData = static_cast<CMarkerHistogram *>(param);
74  Vec3b color = histogramImg.at<Vec3b>(y, x); // BGR
75  histogramImg.release();
76  histogramImg = pUserData->drawHistogram(CV_RGB(color[2], color[1], color[0]));
77  imshow(wndName.c_str(), histogramImg);
78  }, this);
79 }
80 
81 void CMarkerHistogram::close(void) const
82 {
83  destroyWindow(wndName.c_str());
84 }
85 
86 // ======================================== Private ========================================
87 
88 Mat CMarkerHistogram::drawHistogram(Scalar color) const
89 {
90  const byte fMaxHeight = 9; // The maximal number of feature histograms in a column
91  const word nFeatures = m_nodeTrainer.getNumFeatures();
92  const int activeState = getActiveState(color);
93 
94  cv::Size fSize; // Size of the resulting image in feature histograms
95  fSize.width = nFeatures / fMaxHeight;
96  if (nFeatures % fMaxHeight != 0) fSize.width++;
97  fSize.height = (nFeatures < fMaxHeight) ? nFeatures : fMaxHeight;
98 
99  cv::Size resSize; // Size of the resulting image
100  resSize.width = margin.width + fSize.width * (256 + 2 * margin.width);
101  resSize.height = margin.height + fSize.height * (100 + margin.height);
102 
103  // Legende
104  Mat legende = drawLegend(resSize.height - margin.height, activeState);
105  resSize.width += legende.cols;
106 
107  Mat res(resSize, CV_8UC3); // Resulting Image
108  res.setTo(bkgIntencity);
109 
110  for (word f = 0; f < nFeatures; f++) { // freatures
111  int dx = f / fMaxHeight; dx *= (256 + 2 * margin.width);
112  int dy = f % fMaxHeight; dy *= (100 + margin.height);
113 
114  Mat featureHistogram = drawFeatureHistogram(f, activeState);
115  Rect roi(Point(dx, margin.height + dy), featureHistogram.size());
116  featureHistogram.copyTo(res(roi));
117  featureHistogram.release();
118  } // f
119 
120 
121  Rect roi(Point(res.cols - legende.cols, margin.height), legende.size());
122  legende.copyTo(res(roi));
123  legende.release();
124 
125  return res;
126 }
127 
128 Mat CMarkerHistogram::drawHistogram2D(Scalar color) const
129 {
130  const int activeState = getActiveState(color);
131  return drawFeatureHistogram2D(0, activeState);
132 }
133 
134 int CMarkerHistogram::getActiveState(Scalar color) const
135 {
136  size_t nStates = m_vPalette.size();
137  for (byte s = 0; s < nStates; s++) { // states
138  Scalar diff = (Scalar)CV_RGB(bkgIntencity, bkgIntencity, bkgIntencity) + frgWeight * m_vPalette[s].first - color;
139  if (norm(diff) < 1.0) return s;
140  }
141  return -1;
142 }
143 
144 Mat CMarkerHistogram::drawFeatureHistogram(word f, int activeState) const
145 {
146  int x, y;
147  char str[256];
148  const byte nStates = m_nodeTrainer.getNumStates();
149  const int koeff = 1200; // coefficient for histogram value enlargement
150  const size_t n = m_vPalette.size();
151 
152  Mat res(margin.height + 100, 2 * margin.width + 256, CV_8UC3); res.setTo(bkgIntencity);
153  Mat tmp(res.size(), res.type()); tmp.setTo(0);
154 
155  // axis grid
156  for (x = 0; x <= 255; x += 51) {
157  y = ((x > 75) && (x < 180)) ? 25 : 0;
158  line(tmp, Point(margin.width + x, y), Point(margin.width + x, 100), CV_RGB(50, 50, 50));
159  }
160 
161  // histogram
162  if (typeid(m_nodeTrainer) == typeid(CTrainNodeBayes))
163  for (byte s = 0; s < nStates; s++) { // states
164  IPDF *pPDF = dynamic_cast<const CTrainNodeBayes &>(m_nodeTrainer).getPDF(s, f);
165  DGM_ASSERT(pPDF);
166  for (x = 0; x < 256; x++) {
167  int len = static_cast<int>(koeff * pPDF->getDensity(x));
168  y = 100 - MIN(99, len);
169 
170  if ((activeState == -1) || (activeState == s % n))
171  line(tmp, Point(margin.width + x, 100), Point(margin.width + x, y), m_vPalette.at(s % n).first);
172  } // x
173  addWeighted(res, 1.0, tmp, frgWeight, 0.0, res);
174  tmp.setTo(0);
175  } // s
176  tmp.release();
177 
178  // Feature Names
179  if (m_vFeatureNames.empty()) sprintf(str, "feature %d", f);
180  else sprintf(str, "%s", m_vFeatureNames[f].c_str());
181  cv::Size textSize = getTextSize(str, cv::HersheyFonts::FONT_HERSHEY_SIMPLEX, 0.5, 1, NULL);
182  putText(res, str, Point(margin.width + (MAX(256 - textSize.width, 108)) / 2, 16), FONT_HERSHEY_SIMPLEX, 0.5, CV_RGB(125, 125, 125), 1, cv::LineTypes::LINE_AA);
183 
184  // Figure box
185  rectangle(res, Point(margin.width - 1, 0), Point(margin.width + 256, 100), CV_RGB(255,255,255));
186  for (x = 0; x <= 255; x += 51) {
187  sprintf(str, "%d", x);
188  putText(res, str, Point(margin.width + x - 5, 109), FONT_HERSHEY_SIMPLEX, 0.3, CV_RGB(255, 255, 255), 1, cv::LineTypes::LINE_AA);
189  }
190 
191  return res;
192 }
193 
194 Mat CMarkerHistogram::drawFeatureHistogram2D(word f, int activeState) const
195 {
196  char str[256];
197  const byte nStates = m_nodeTrainer.getNumStates();
198  const word nFeatures = m_nodeTrainer.getNumFeatures();
199  const int koeff = 1000000; // coefficient for histogram value enlargement
200  const size_t n = m_vPalette.size();
201 
202  Mat res(2 * margin.height + 256, 2 * margin.height + 256, CV_8UC3); res.setTo(bkgIntencity);
203 // rectangle(res, Point(margin.height, margin.height), Point(256 + margin.height, 256 + margin.height), CV_RGB(0, 0, 0), -1);
204  Mat tmp(res.size(), res.type()); tmp.setTo(0);
205 
206  // histogram
207  if ((typeid(m_nodeTrainer) == typeid(CTrainNodeBayes)) && (nFeatures == 2)) {
208  for (byte s = 0; s < nStates; s++) { // states
209  IPDF *pPDF2D = dynamic_cast<const CTrainNodeBayes &>(m_nodeTrainer).getPDF2D(s);
210  DGM_ASSERT(pPDF2D);
211  for (int y = 0; y < 256; y++) {
212  Vec3b *pTmp = tmp.ptr<Vec3b>(margin.height + 256 - y);
213  for (int x = 0; x < 256; x++) {
214  double val = MIN(255, koeff * pPDF2D->getDensity(Scalar(x, y)));
215  Scalar color = val * m_vPalette[s % n].first / 255;
216 
217  if ((activeState == -1) || (activeState == s % n))
218  pTmp[margin.height + x] = Vec3b((byte)color[0], (byte)color[1], (byte)color[2]);
219  }
220  }
221  addWeighted(res, 1.0, tmp, frgWeight, 0.0, res);
222  tmp.setTo(0);
223  } // s
224  tmp.release();
225  }
226  else DGM_WARNING("The node trainer (%s) is not Bayes or the number of features (%d) is not 2", typeid(m_nodeTrainer).name(), nFeatures);
227 
228  // Feature Names
229  tmp = Mat(margin.height, res.rows, CV_8UC3);
230  tmp.setTo(bkgIntencity);
231  sprintf(str, " feature 1 255");
232  putText(tmp, str, Point(3, tmp.rows - 4), FONT_HERSHEY_SIMPLEX, 0.3, CV_RGB(255, 255, 255), 1, cv::LineTypes::LINE_AA);
233  flip(tmp.t(), tmp, 0);
234  tmp.copyTo(res(Rect(0, 0, margin.height, tmp.rows)));
235  sprintf(str, "0 feature 0 255");
236  putText(res, str, Point(3, res.rows - 6), FONT_HERSHEY_SIMPLEX, 0.3, CV_RGB(255, 255, 255), 1, cv::LineTypes::LINE_AA);
237 
238  // Figure box
239  rectangle(res, Point(margin.height - 1, margin.height - 1), Point(256 + margin.height, 256 + margin.height), CV_RGB(255, 255, 255));
240 
241  return res;
242 }
243 
244 Mat CMarkerHistogram::drawLegend(int maxHeight, int activeState) const
245 {
246  char str[256];
247  const byte sMaxHeight = maxHeight / (2 * margin.height); // The maximal number of states in a column
248  const byte nStates = m_nodeTrainer.getNumStates();
249  const size_t n = m_vPalette.size();
250 
251  cv::Size sSize; // Size of the resulting image in states
252  sSize.width = (nStates + 1) / sMaxHeight;
253  if ((nStates + 1) % sMaxHeight != 0) sSize.width++;
254  sSize.height = ((nStates + 1) < sMaxHeight) ? (nStates + 1) : sMaxHeight;
255 
256  cv::Size resSize; // Size of the resulting image
257  resSize.width = sSize.width * 120;
258  resSize.height = 2 * margin.height * sSize.height;
259 
260  Mat res(resSize, CV_8UC3); res.setTo(bkgIntencity);
261  Mat tmp(res.size(), res.type()); tmp.setTo(0);
262 
263  for (byte s = 0; s < nStates; s++) { // states
264  int dx = s / sMaxHeight; dx *= 120;
265  int dy = s % sMaxHeight; dy *= 2 * margin.height;
266 
267  // Color box
268  rectangle(tmp, Point(dx, dy), Point(dx + margin.width, dy + margin.height), m_vPalette.at(s % n).first, -1);
269 
270  // Active triangle
271  if (activeState == s) {
272  const Point triangle[3] = {Point(dx, dy), Point(dx + margin.height / 2, dy + margin.height / 2), Point(dx, dy + margin.height)};
273  fillConvexPoly(tmp, triangle, 3, CV_RGB(0, 0, 0));
274  }
275 
276  // Class name
277  if (m_vPalette.at(s % n).second.empty()) sprintf(str, "c%d", s);
278  else sprintf(str, "%s", m_vPalette.at(s % n).second.c_str());
279  putText(tmp, str, Point(dx + margin.width + 5, dy + margin.height - 3), FONT_HERSHEY_SIMPLEX, 0.5, CV_RGB(255, 255, 255), 1, cv::LineTypes::LINE_AA);
280  } // s
281 
282  // White "all" color box
283  int dx = nStates / sMaxHeight; dx *= 120;
284  int dy = nStates % sMaxHeight; dy *= 2 * margin.height;
285  rectangle(tmp, Point(dx, dy), Point(dx + margin.width, dy + margin.height), CV_RGB(255, 255, 255), -1);
286  if (activeState == -1) {
287  const Point triangle[3] = {Point(dx, dy), Point(dx + margin.height / 2, dy + margin.height / 2), Point(dx, dy + margin.height)};
288  fillConvexPoly(tmp, triangle, 3, CV_RGB(0, 0, 0));
289  }
290  putText(tmp, "all", Point(dx + margin.width + 5, dy + margin.height - 3), FONT_HERSHEY_SIMPLEX, 0.5, CV_RGB(255, 255, 255), 1, cv::LineTypes::LINE_AA);
291 
292  addWeighted(res, 1.0, tmp, frgWeight, 0.0, res);
293  tmp.release();
294 
295  return res;
296 }
297 
298 } }
void close(void) const
Closes the histogram window.
virtual double getDensity(Scalar point)=0
Returns the probability density value for the argument point.
word getNumFeatures(void) const
Returns number of features.
Definition: ITrain.h:37
Mat drawHistogram(void) const
Draws a figure with the visualization of feature densitiy distributions.
Mat drawFeatureHistogram(word f, int activeState=-1) const
Draws a single feature histogram.
Mat drawHistogram2D(void) const
Draws a figure with the visualization of 2-dimensional node potentials histogram. ...
Mat getNodePotentials(const Mat &featureVectors, const Mat &weights=Mat(), float Z=0.0f) const
Returns a block of node potentials, based on the block of feature vector.
Definition: TrainNode.cpp:54
const CTrainNode & m_nodeTrainer
The node trainer.
void showHistogram(void)
Visualizes the feature densitiy distributions in a separate window with user interaction.
Mat drawClassificationMap2D(float Z) const
Draws a figure with the visualization of 2-dimensional classification map.
Interface class for Probability Density Function (PDF)
Definition: IPDF.h:16
Mat drawLegend(int maxHeight, int activeState=-1) const
Draws a legend to the main figure.
Mat drawFeatureHistogram2D(word f, int activeState=-1) const
Draws a 2-dimensional feature histogram.
byte getNumStates(void) const
Returns number of states (classes)
int getActiveState(Scalar color) const
Retrieves a chosen by an user state, from the color.
vec_nColor_t m_vPalette
Pointer to the container with the palette.
Definition: Marker.h:136