Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpColorHistogram.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 */
30
31#include <visp3/rbt/vpColorHistogram.h>
32
33#include <visp3/core/vpImage.h>
34#include <visp3/core/vpRect.h>
35
37
39{
40 if (histogram.getBinNumber() != m_N) {
41 throw vpException(vpException::dimensionError, "Different number of bins between builder and histogram when building histogram");
42 }
43 unsigned int count = 0;
44 for (unsigned int i = 0; i < m_counts.size(); ++i) {
45 count += m_counts[i];
46 }
47 const float countFloat = static_cast<float>(count);
48 for (unsigned int i = 0; i < m_counts.size(); ++i) {
49 histogram.m_probas[i] = static_cast<float>(m_counts[i]) / countFloat;
50 }
51 histogram.m_numPixels = count;
52}
53
54vpColorHistogram::vpColorHistogram() : m_N(0), m_binSize(0), m_numPixels(0)
55{ }
56
58{
59 setBinNumber(N);
60}
61
63{
64 if (N != 1 && N != 2 && N != 4 && N != 8 && N != 16 && N != 32 && N != 64 && N != 128) {
65 throw vpException(vpException::badValue, "The number of bins per component should be a power of 2 (below or equal to 128)");
66 }
67 m_N = N;
68 m_binSize = 256 / m_N;
69 m_numPixels = 0;
70 m_probas = std::vector<float>(N * N * N, 0.f);
71}
72
74{
75 std::vector<unsigned int> histo(m_N * m_N * m_N, 0);
76 m_probas.resize(m_N * m_N * m_N);
77 unsigned int pixels = 0;
78 for (unsigned int i = 0; i < image.getSize(); ++i) {
79 if (mask.bitmap[i]) {
80 unsigned int index = colorToIndex(image.bitmap[i]);
81 ++histo[index];
82 ++pixels;
83 }
84 }
85 m_numPixels = pixels;
86 for (unsigned int i = 0; i < histo.size(); ++i) {
87 m_probas[i] = static_cast<float>(histo[i]) / pixels;
88 }
89}
90
91void vpColorHistogram::build(const std::vector<unsigned int> &counts)
92{
93 if (m_probas.size() != counts.size()) {
94 throw vpException(vpException::dimensionError, "Number of bins are not the same");
95 }
96 m_probas.resize(m_N * m_N * m_N);
97 m_numPixels = 0;
98 for (unsigned int count : counts) {
99 m_numPixels += count;
100 }
101 for (unsigned int i = 0; i < m_probas.size(); ++i) {
102 m_probas[i] = static_cast<float>(counts[i]) / m_numPixels;
103 }
104}
105
106void vpColorHistogram::merge(const vpColorHistogram &other, float alpha)
107{
108 if (other.m_N != m_N) {
109 throw vpException(vpException::badValue, "Histograms should have same dimensions");
110 }
111 if (m_numPixels == 0) {
112 m_probas = other.m_probas;
113 m_numPixels = other.m_numPixels;
114 }
115 else {
116 float malpha = 1.f - alpha;
117
118 for (unsigned int i = 0; i < m_probas.size(); ++i) {
119 m_probas[i] = malpha * m_probas[i] + alpha * other.m_probas[i];
120 }
121 }
122}
123
125{
126 proba.resize(image.getHeight(), image.getWidth());
127#ifdef VISP_HAVE_OPENMP
128#pragma omp parallel for
129#endif
130 for (int i = 0; i < static_cast<int>(image.getSize()); ++i) {
131 proba.bitmap[i] = m_probas[colorToIndex(image.bitmap[i])];
132 }
133}
134
135void vpColorHistogram::computeProbas(const vpImage<vpRGBa> &image, vpImage<float> &proba, const vpRect &bb) const
136{
137 proba.resize(image.getHeight(), image.getWidth(), 0.f);
138 const int h = static_cast<int>(image.getHeight()), w = static_cast<int>(image.getWidth());
139 const int top = static_cast<int>(bb.getTop());
140 const int left = static_cast<int>(bb.getLeft());
141 const int bottom = std::min(h- 1, static_cast<int>(bb.getBottom()));
142 const int right = std::min(w - 1, static_cast<int>(bb.getRight()));
143#ifdef VISP_HAVE_OPENMP
144#pragma omp parallel for
145#endif
146 for (int i = top; i <= bottom; ++i) {
147 const vpRGBa *colorRow = image[i];
148 float *probaRow = proba[i];
149 for (int j = left; j <= right; ++j) {
150 probaRow[j] = m_probas[colorToIndex(colorRow[j])];
151 }
152 }
153}
154
155double vpColorHistogram::kl(const vpColorHistogram &other) const
156{
157 if (other.m_N != m_N) {
158 throw vpException(vpException::badValue, "Histograms should have same dimensions");
159 }
160 double divergence = 0.0;
161 for (unsigned int i = 0; i < m_probas.size(); ++i) {
162 if (other.m_probas[i] > 0.0 && m_probas[i] > 0.0) {
163 divergence += m_probas[i] * log(m_probas[i] / other.m_probas[i]);
164 }
165 }
166 return divergence;
167}
168
169double vpColorHistogram::jsd(const vpColorHistogram &other) const
170{
171 vpColorHistogram mixture(m_N);
172
173 for (unsigned int i = 0; i < m_probas.size(); ++i) {
174 mixture.m_probas[i] = m_probas[i] * 0.5f + other.m_probas[i] * 0.5f;
175 }
176 // JSD = 0.5KL(P || M) + 0.5(Q||M) where M is the average mixture distrib of P and Q
177 return (kl(mixture) + other.kl(mixture)) / 2.0;
178}
179
181{
182 double bcoeff = 0.0;
183
184 for (unsigned int i = 0; i < m_probas.size(); ++i) {
185 bcoeff += sqrt(m_probas[i] * other.m_probas[i]);
186 }
187
188 return sqrt(1.0 - bcoeff);
189}
190
192{
193 if (insideMask.m_N != outsideMask.m_N) {
194 throw vpException(vpException::badValue, "Histograms should have same number of bins");
195 }
196
197 unsigned int bins = static_cast<unsigned int>(insideMask.m_probas.size());
198
199 std::vector<unsigned int> countsIn(bins, 0), countsOut(bins, 0);
200
201//#pragma omp parallel
202 {
203 std::vector<unsigned int>localCountsIn(bins, 0), localCountsOut(bins, 0);
204//#pragma omp for schedule(static, 1024)
205 for (unsigned int i = 0; i < image.getSize(); ++i) {
206 unsigned int index = insideMask.colorToIndex(image.bitmap[i]);
207 localCountsIn[index] += (mask.bitmap[i] > 0);
208 localCountsOut[index] += (mask.bitmap[i] == 0);
209 }
210//#pragma omp critical
211 {
212 for (unsigned int i = 0; i < bins; ++i) {
213 countsIn[i] += localCountsIn[i];
214 countsOut[i] += localCountsOut[i];
215 }
216 }
217 }
218 insideMask.build(countsIn);
219 outsideMask.build(countsOut);
220}
221
222void vpColorHistogram::computeSplitHistograms(const vpImage<vpRGBa> &image, const vpImage<bool> &mask, const vpRect &bbInside, vpColorHistogram &insideMask, vpColorHistogram &outsideMask)
223{
224 if (insideMask.m_N != outsideMask.m_N) {
225 throw vpException(vpException::badValue, "Histograms should have same number of bins");
226 }
227
228 const unsigned int bins = static_cast<unsigned int>(insideMask.m_probas.size());
229
230 std::vector<unsigned int> countsIn(bins, 0), countsOut(bins, 0);
231
232 const int beforeBBStart = static_cast<int>(bbInside.getTop()) * image.getWidth() + static_cast<int>(bbInside.getLeft());
233 const int afterBBEnd = static_cast<int>(bbInside.getBottom()) * image.getWidth() + static_cast<int>(bbInside.getRight());
234#ifdef VISP_HAVE_OPENMP
235#pragma omp parallel
236#endif
237 {
238 std::vector<unsigned int>localCountsIn(bins, 0), localCountsOut(bins, 0);
239#ifdef VISP_HAVE_OPENMP
240#pragma omp for
241#endif
242 for (int i = 0; i < beforeBBStart; ++i) {
243 const unsigned int index = insideMask.colorToIndex(image.bitmap[i]);
244 ++localCountsOut[index];
245 }
246#ifdef VISP_HAVE_OPENMP
247#pragma omp for
248#endif
249 for (int i = afterBBEnd; i < static_cast<int>(image.getSize()); ++i) {
250 const unsigned int index = insideMask.colorToIndex(image.bitmap[i]);
251 ++localCountsOut[index];
252 }
253
254#ifdef VISP_HAVE_OPENMP
255#pragma omp for
256#endif
257 for (int i = static_cast<int>(bbInside.getTop()); i < static_cast<int>(round(bbInside.getBottom())); ++i) {
258 for (int j = static_cast<int>(bbInside.getLeft()); j < static_cast<int>(round(bbInside.getRight())); ++j) {
259 const unsigned int bitmapIndex = i * image.getWidth() + j;
260 const unsigned int index = insideMask.colorToIndex(image.bitmap[bitmapIndex]);
261 const bool pixelInMask = mask.bitmap[bitmapIndex] > 0;
262 localCountsIn[index] += static_cast<unsigned int>(pixelInMask);
263 localCountsOut[index] += static_cast<unsigned int>(!pixelInMask);
264 }
265 }
266#ifdef VISP_HAVE_OPENMP
267#pragma omp critical
268#endif
269 {
270 for (unsigned int i = 0; i < bins; ++i) {
271 countsIn[i] += localCountsIn[i];
272 countsOut[i] += localCountsOut[i];
273 }
274 }
275 }
276 insideMask.build(countsIn);
277 outsideMask.build(countsOut);
278}
279
280END_VISP_NAMESPACE
void build(vpColorHistogram &histogram)
double kl(const vpColorHistogram &other) const
void build(const vpImage< vpRGBa > &image, const vpImage< bool > &mask)
Build the histogram representation and associated color probabilities given an image and a mask....
double hellinger(const vpColorHistogram &other) const
unsigned int getBinNumber() const
static void computeSplitHistograms(const vpImage< vpRGBa > &image, const vpImage< bool > &mask, vpColorHistogram &inMask, vpColorHistogram &outsideMask)
void setBinNumber(unsigned int N)
Change the number of bins per color component that the histogram has After calling this method,...
void computeProbas(const vpImage< vpRGBa > &image, vpImage< float > &proba) const
Compute the probabilities of every pixel according to this histogram.
unsigned int colorToIndex(const vpRGBa &p) const
Convert an RGB color to an index that can be used to retrieve the probability of this color The alpha...
void merge(const vpColorHistogram &other, float alpha)
Merge this histogram with an another. This histogram is modified. The probabilities are interpolated ...
double jsd(const vpColorHistogram &other) const
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
@ dimensionError
Bad dimension.
Definition vpException.h:71
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getWidth() const
Definition vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition vpImage.h:544
unsigned int getSize() const
Definition vpImage.h:221
Type * bitmap
points toward the bitmap
Definition vpImage.h:135
unsigned int getHeight() const
Definition vpImage.h:181
Defines a rectangle in the plane.
Definition vpRect.h:79
double getLeft() const
Definition vpRect.h:173
double getRight() const
Definition vpRect.h:179
double getBottom() const
Definition vpRect.h:97
double getTop() const
Definition vpRect.h:192