Basic color reduction algorithm in OpenCV - c++

Here is the formula for basic color reduction in OpenCV that I read in a book.
//p is the value of the channel of a pixel
//n is the reduction factor
p = (p/n)*n + n/2; //integer division
I understand that the first expression (p/n)*n is the first multiple of n less than or equal to p. I think that just this is enough for color reduction by the factor of n. But I don't get what is the need to add n/2 to it.
And also, if p=255 and n=5, this formula will try to set p = 257, won't it overflow the range of p?
Please explain.

With n=255, (((p)/n)*n) would return 0 for p < 255, and 255 for p == 255.
To be balanced you would want to return 0 for p < 128 and 255 for p >= 128. This can be achieved with:
((p + n/2) / n) * n
This is a bit like ((p / n) + 0.5) * n, except that you obviously can't do that. The effect is to give rounding, instead of truncation on the the result of p/n. Actually this only works well for some values of n.
An easy way to get exactly what you want would be to use a lookup table.
I agree ((p)/n)*n + n/2 has problems, as pointed out by old-ufo

They say that n/2 is needed to have "central" position instead of minimum, but you are right it looks like not needed.
Lets assume I want to have 2 colors - black and white, so n=255. Without n/2 I would have 0 and 255 as a result. With n/2 - 127 and 382, which seems not correct.

Related

Compute integer bounds to include scaled floating point values

I am trying to compute integer array bounds that will include floating point limits divided by a scale. For example, if my origin is 0, my floating point maximum is 10 then my integer array bounds need to be 2. The obvious formula is to divide my bounds by the scale, giving the incorrect result of 1.
I need to divide the inclusive maximum values by the scale and add one if the division is an exact multiple.
I am running into a mismatch between the normal way to define and use integer array indexes and my desired way to use real value coordinates. I am trying to map inclusive real value coordinates into integer array indexes, using a scaling term.
(I am actually working with two dimensional maps, but the problem can be expressed more simply in one dimension.)
This is wrong:
int get_array_size(double, scale, double maximum)
{
return std::ceil(maximum / scale); // Fails on exact multiples
}
This is wasteful:
int get_array_size(double, scale, double maximum)
{
return 1 + std::ceil(maximum / scale); // Allocates extra array memory
}
This is ugly and I am not sure if it is correct:
int get_array_size(double, scale, double maximum)
{
if (maximum % scale == 0) // I am not sure if this is correct
return 1 + std::ceil(maximum / scale);
else
return std::ceil(maximum / scale); // Maybe I can eliminate the call to std::ceil?
}
I am trying to get the value maximum / scale on every open ended interval ending at multiples of scale and 1 + maximum / scale on every interval from >= multiple of scale ending at < multiple of scale + 1. I am not sure how to correctly express this in mathematical terms or how to implement it in c++. I would be grateful if someone can clarify my understand and point me in the right direction.
Mathematically I think I am trying to define f(x, s) = y s.t. if s * n <= x and x < s * (n + 1) then y = n + 1. I want to implement this efficiently and respect the difference between <= and < comparison.
The way I interpret this question, I think maximum and scale don't actually matter - what you are really asking about is how to correctly map from floats to ints with specific boundary conditions. For example [0.0, 1.0) to 0, [1.0, 2.0) to 1, etc. So the question becomes a bit simpler if we just consider maximum / scale to be a single quantity; I'll call it t.
I believe you actually want to use std::floor instead of std::ceil:
int scaled_coord_to_index(float t) {
return std::floor(t);
}
And the size of your array should always be the maximum scaled coordinate + 1 (with negative values normalized to start at 0).
int array_size(float min_t, float max_t) {
// NOTE: This will "anchor" your coords based on the most negative value.
// e.g. if that value is 1.6, then your bins will be [1.6, 2.6), [2.6, 3.6), etc.
// To change that behavior you could use std::floor(min_t) instead.
return scaled_coord_to_index(max_t - min_t) + 1;
}

How to plot lines according to some condition?

I was solving a coding problem and came across this one. It states :
We have an infinitely planar cartesian coordinate system on which N points are plotted. Cartesian coordinates of the point I am represented by (Xi, Yi).
Now we want to draw (N-1) line segments which may have arbitrary lengths and the points need not lie on the lines. The slope of each line must be 1 or -1.
Let's denote the minimum distance we have to walk from a point I to reach a line by Di and let's say a = max(D1, D2, D3,..., DN). We want this distance to be minimum as possible.
Thus we have to plot lines in such a way that it minimizes 'a' and compute a*sqrt(2)
Constraints :
1 <= T <= 100
2 <= N <= 10^4
|Xi|, |Yi| <= 10^9 for each valid i
Here T denotes number of test cases.
Sample input 1 :
N = 3
Points : (0,0) , (0,1) , (0,-1)
Sample output 1 :
0.5
Explanation: We should draw lines described by equations y−x+0.5=0 and y−x−0.5=0
Sample input 2 :
N = 3
Points : (0,1) , (1,0) , (-1,0)
Sample output 2 :
0
Explanation: We should draw lines described by equations y−x−1=0 and y+x−1=0
Output format :
For each test case, print a single line containing one real number — the minimum distance a multiplied by sqrt(2). Your answer will be considered correct if its absolute or relative error does not exceed 10^(-6).
Time limit: 1 sec
My understanding is as the slopes are 1 or -1 the equations of the lines would be y = x + c or y = -x + c and we just have to find the y-intercept c which minimizes the distance 'a' in the problem. Also, the minimum distance from a point to the line is the length of the perpendicular to the line.
So I am having difficulty to devise an algorithm which will check all possible values of 'c' and find the optimal one.
Let us denote M[i] the point (x[i], y[i])
The fist step is to compute the distance between a point M(x, y) and a line D, slope of which is equal to +/-1.
Let us denote D and D' the lines
D: y + x + c = 0
D': y - x + c = 0
Then, a few calculations allow to show that
the distance between M and D is equal to d(M, D) = abs(y + x + c)/sqrt(2)
the distance between M and D' is equal to d(M, D') = abs(y - x + c)/sqrt(2)
Let us now consider two different points, for example M[0] and M[1], and let us calculate the minimum distance between these two points and a line D of parameter c and slope +/-1.
Formally, we have two find the minimum, over c and slope, of
max(d(M[0], D), d(M[1], D))
If the slope is -1, i.e. if the equation is y+x+c=0, one can easily show the the optimum c parameter is equal to
c = -(x0 + y0 + x1 + y1)/2
The corresponding distance is equal to abs(x0+y0-x1-y1)/(2*sqrt(2))
If the slope is 1, i.e. if the equation is y-x+c=0, one can show the the optimum c parameter is equal to
c = (x0 - y0 + x1 - y1)/2
The corresponding distance is equal to abs(y0 - x0 - y1 + x1)/(2*sqrt(2))
Therefore, the minimum distance from these two points to an optimal line is the minimum of the previous two distances.
This leads to define the following quantities, for each points M[i]:
a|i] = y[i] - x[i]
b[i] = y[i] + x[i]
And then to define a distance between points M[i] and M[j] as :
d(M[i], M[j]) = min (abs(b[i]-b[j]), abs(a[i]-a[j]))
The proposed algorithm consists in finding the pair (M[i], M[j]) such that this distance is minimized.
Then the wanted result is equal to half this distance.
This corresponds to consider that a line will pass through the distant points (according to the defined distance), except the two closest ones, for which we will draw a line just in between.
(EDIT)
The complexity is not O(n^2) as previously stated.
The complexity to find the min of d(M[i], M[j]) is O(N logN).
This is obtained by sorting the a[i] and to get the min of the differences between adjacent values, i.e. min(a[i+1] - a[i]).
Then by doing the same for the b[i], and finally taking the minimum of the two obtained values.

Subsampling an array of numbers

I have a series of 100 integer values which I need to reduce/subsample to 77 values for the purpose of fitting into a predefined space on screen. This gives a fraction of 77/100 values-per-pixel - not very neat.
Assuming the 77 is fixed and cannot be changed, what are some typical techniques for subsampling 100 numbers down to 77. I get a sense that it will be a jagged mapping, by which I mean the first new value is the average of [0, 1] then the next value is [3], then average [4, 5] etc. But how do I approach getting the pattern for this mapping?
I am working in C++, although I'm more interested in the technique than implementation.
Thanks in advance.
Either if you downsample or you oversample, you are trying to reconstruct a signal over nonsampled points in time... so you have to make some assumptions.
The sampling theorem tells you that if you sample a signal knowing that it has no frequency components over half the sampling frequency, you can continously and completely recover the signal over the whole timing period. There's a way to reconstruct the signal using sinc() functions (this is sin(x)/x)
sinc() (indeed sin(M_PI/Sampling_period*x)/M_PI/x) is a function that has the following properties:
Its value is 1 for x == 0.0 and 0 for x == k*Sampling_period with k == 0, +-1, +-2, ...
It has no frequency component over half of the sampling_frequency derived from Sampling_period.
So if you consider the sum of the functions F_x(x) = Y[k]*sinc(x/Sampling_period - k) to be the sinc function that equals the sampling value at position k and 0 at other sampling value and sum over all k in your sample, you'll get the best continous function that has the properties of not having components on frequencies over half the sampling frequency and have the same values as your samples set.
Said this, you can resample this function at whatever position you like, getting the best way to resample your data.
This is by far, a complicated way of resampling data, (it has also the problem of not being causal, so it cannot be implemented in real time) and you have several methods used in the past to simplify the interpolation. you have to constructo all the sinc functions for each sample point and add them together. Then you have to resample the resultant function to the new sampling points and give that as a result.
Next is an example of the interpolation method just described. It accepts some input data (in_sz samples) and output interpolated data with the method described before (I supposed the extremums coincide, which makes N+1 samples equal N+1 samples, and this makes the somewhat intrincate calculations of (in_sz - 1)/(out_sz - 1) in the code (change to in_sz/out_sz if you want to make plain N samples -> M samples conversion:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
/* normalized sinc function */
double sinc(double x)
{
x *= M_PI;
if (x == 0.0) return 1.0;
return sin(x)/x;
} /* sinc */
/* interpolate a function made of in samples at point x */
double sinc_approx(double in[], size_t in_sz, double x)
{
int i;
double res = 0.0;
for (i = 0; i < in_sz; i++)
res += in[i] * sinc(x - i);
return res;
} /* sinc_approx */
/* do the actual resampling. Change (in_sz - 1)/(out_sz - 1) if you
* don't want the initial and final samples coincide, as is done here.
*/
void resample_sinc(
double in[],
size_t in_sz,
double out[],
size_t out_sz)
{
int i;
double dx = (double) (in_sz-1) / (out_sz-1);
for (i = 0; i < out_sz; i++)
out[i] = sinc_approx(in, in_sz, i*dx);
}
/* test case */
int main()
{
double in[] = {
0.0, 1.0, 0.5, 0.2, 0.1, 0.0,
};
const size_t in_sz = sizeof in / sizeof in[0];
const size_t out_sz = 5;
double out[out_sz];
int i;
for (i = 0; i < in_sz; i++)
printf("in[%d] = %.6f\n", i, in[i]);
resample_sinc(in, in_sz, out, out_sz);
for (i = 0; i < out_sz; i++)
printf("out[%.6f] = %.6f\n", (double) i * (in_sz-1)/(out_sz-1), out[i]);
return EXIT_SUCCESS;
} /* main */
There are different ways of interpolation (see wikipedia)
The linear one would be something like:
std::array<int, 77> sampling(const std::array<int, 100>& a)
{
std::array<int, 77> res;
for (int i = 0; i != 76; ++i) {
int index = i * 99 / 76;
int p = i * 99 % 76;
res[i] = ((p * a[index + 1]) + ((76 - p) * a[index])) / 76;
}
res[76] = a[99]; // done outside of loop to avoid out of bound access (0 * a[100])
return res;
}
Live example
Create 77 new pixels based on the weighted average of their positions.
As a toy example, think about the 3 pixel case which you want to subsample to 2.
Original (denote as multidimensional array original with RGB as [0, 1, 2]):
|----|----|----|
Subsample (denote as multidimensional array subsample with RGB as [0, 1, 2]):
|------|------|
Here, it is intuitive to see that the first subsample seems like 2/3 of the first original pixel and 1/3 of the next.
For the first subsample pixel, subsample[0], you make it the RGB average of the m original pixels that overlap, in this case original[0] and original[1]. But we do so in weighted fashion.
subsample[0][0] = original[0][0] * 2/3 + original[1][0] * 1/3 # for red
subsample[0][1] = original[0][1] * 2/3 + original[1][1] * 1/3 # for green
subsample[0][2] = original[0][2] * 2/3 + original[1][2] * 1/3 # for blue
In this example original[1][2] is the green component of the second original pixel.
Keep in mind for different subsampling you'll have to determine the set of original cells that contribute to the subsample, and then normalize to find the relative weights of each.
There are much more complex graphics techniques, but this one is simple and works.
Everything depends on what you wish to do with the data - how do you want to visualize it.
A very simple approach would be to render to a 100-wide image, and then smooth scale the image down to a narrower size. Whatever graphics/development framework you're using will surely support such an operation.
Say, though, that your goal might be to retain certain qualities of the data, such as minima and maxima. In such a case, for each bin, you're drawing a line of darker color up to the minimum value, and then continue with a lighter color up to the maximum. Or, you could, instead of just putting a pixel at the average value, you draw a line from the minimum to the maximum.
Finally, you might wish to render as if you had 77 values only - then the goal is to somehow transform the 100 values down to 77. This will imply some kind of an interpolation. Linear or quadratic interpolation is easy, but adds distortions to the signal. Ideally, you'd probably want to throw a sinc interpolator at the problem. A good list of them can be found here. For theoretical background, look here.

Select K random lines from a text file

This is an extension of the original question of selecting a random line from a text of X lines where the probability of the text line selected is 1/X. The trick is to select the Jth line if you query a random variable Y with a range of [0,1) and it returns a value less than 1/J.
Now in this new version of the problem we have to select K random lines where K is less than X. I believe the probability for each line should be K/X.
I'm stuck on how to extend the original solution to K lines. Is it possible? any explanations would be great.
This can be solved using a generalization of the original algorithm. The intuition is as follows: maintain a list of k candidate lines from the file, which are initially seeded to the first k lines. Then, from that point forward, upon seeing the nth line of the file:
Choose a random value x between 1 and n, inclusive.
If x > k, ignore this element.
Otherwise, replace element x with the nth line of the file.
The proof that this correctly samples each element with probability k / n, where n is the total number of lines in the file, is as follows. Assume that n ≥ k. We prove by induction that each element has probability k / n of being picked by showing that after seeing z elements, each of those elements has probability k / z of being chosen. In particular, this means that after seeing n elements, each has probability k / n as required.
As our inductive basis, if we see exactly k elements, then each is picked. Thus the probability of being chosen is k / k, as required.
For the inductive step, assume that for some z ≥ k, each of the first z elements have been chosen with probability k / z and consider the (z + 1)st element. We choose a random natural number in the range [1, z + 1]. With probability k / (z + 1), we decide to choose this element, then evict some old element. This means that the new element is chosen with probability k / (z + 1). For each of the z original elements, the probability that it is chosen at this point is then the probability that we had chosen it after the first z elements were inspected (probability k / z, by our inductive hypothesis), and the probability that we retain it is z / (z + 1), since we replace it with probability 1 / (z + 1). Thus the new probability that it is chosen is (k / z) (z / (z + 1)) = k / (z + 1). Thus all of the first z + 1 elements are chosen with probability k / (z + 1), completing the induction.
Moreover, this algorithm runs in O(n) time and uses only O(k) space, meaning that the runtime is independent of the value of k. To see this, note that each iteration does O(1) work, and there are a total of O(n) interations.
If you're curious, I have an implementation of this algorithm as a C++ STL-style algorithm available here on my personal site.
Hope this helps!
First select the first element randomly out of the X lines using the first algorithm. Then select the second out of the remaining X-1 lines. Run this process K times.
The probability of any given set of K lines is (X choose K). I'll leave it up to you to verify that this algorithm gives the desired uniform distribution.

Probability density function from a paper, implemented using C++, not working as intended

So i'm implementing a heuristic algorithm, and i've come across this function.
I have an array of 1 to n (0 to n-1 on C, w/e). I want to choose a number of elements i'll copy to another array. Given a parameter y, (0 < y <= 1), i want to have a distribution of numbers whose average is (y * n). That means that whenever i call this function, it gives me a number, between 0 and n, and the average of these numbers is y*n.
According to the author, "l" is a random number: 0 < l < n . On my test code its currently generating 0 <= l <= n. And i had the right code, but i'm messing with this for hours now, and i'm lazy to code it back.
So i coded the first part of the function, for y <= 0.5
I set y to 0.2, and n to 100. That means it had to return a number between 0 and 99, with average 20.
And the results aren't between 0 and n, but some floats. And the bigger n is, smaller this float is.
This is the C test code. "x" is the "l" parameter.
//hate how code tag works, it's not even working now
int n = 100;
float y = 0.2;
float n_copy;
for(int i = 0 ; i < 20 ; i++)
{
float x = (float) (rand()/(float)RAND_MAX); // 0 <= x <= 1
x = x * n; // 0 <= x <= n
float p1 = (1 - y) / (n*y);
float p2 = (1 - ( x / n ));
float exp = (1 - (2*y)) / y;
p2 = pow(p2, exp);
n_copy = p1 * p2;
printf("%.5f\n", n_copy);
}
And here are some results (5 decimals truncated):
0.03354
0.00484
0.00003
0.00029
0.00020
0.00028
0.00263
0.01619
0.00032
0.00000
0.03598
0.03975
0.00704
0.00176
0.00001
0.01333
0.03396
0.02795
0.00005
0.00860
The article is:
http://www.scribd.com/doc/3097936/cAS-The-Cunning-Ant-System
pages 6 and 7.
or search "cAS: cunning ant system" on google.
So what am i doing wrong? i don't believe the author is wrong, because there are more than 5 papers describing this same function.
all my internets to whoever helps me. This is important to my work.
Thanks :)
You may misunderstand what is expected of you.
Given a (properly normalized) PDF, and wanting to throw a random distribution consistent with it, you form the Cumulative Probability Distribution (CDF) by integrating the PDF, then invert the CDF, and use a uniform random predicate as the argument of the inverted function.
A little more detail.
f_s(l) is the PDF, and has been normalized on [0,n).
Now you integrate it to form the CDF
g_s(l') = \int_0^{l'} dl f_s(l)
Note that this is a definite integral to an unspecified endpoint which I have called l'. The CDF is accordingly a function of l'. Assuming we have the normalization right, g_s(N) = 1.0. If this is not so we apply a simple coefficient to fix it.
Next invert the CDF and call the result G^{-1}(x). For this you'll probably want to choose a particular value of gamma.
Then throw uniform random number on [0,n), and use those as the argument, x, to G^{-1}. The result should lie between [0,1), and should be distributed according to f_s.
Like Justin said, you can use a computer algebra system for the math.
dmckee is actually correct, but I thought that I would elaborate more and try to explain away some of the confusion here. I could definitely fail. f_s(l), the function you have in your pretty formula above, is the probability distribution function. It tells you, for a given input l between 0 and n, the probability that l is the segment length. The sum (integral) for all values between 0 and n should be equal to 1.
The graph at the top of page 7 confuses this point. It plots l vs. f_s(l), but you have to watch out for the stray factors it puts on the side. You notice that the values on the bottom go from 0 to 1, but there is a factor of x n on the side, which means that the l values actually go from 0 to n. Also, on the y-axis there is a x 1/n which means these values don't actually go up to about 3, they go to 3/n.
So what do you do now? Well, you need to solve for the cumulative distribution function by integrating the probability distribution function over l which actually turns out to be not too bad (I did it with the Wolfram Mathematica Online Integrator by using x for l and using only the equation for y <= .5). That however was using an indefinite integral and you are really integration along x from 0 to l. If we set the resulting equation equal to some variable (z for instance), the goal now is to solve for l as a function of z. z here is a random number between 0 and 1. You can try using a symbolic solver for this part if you would like (I would). Then you have not only achieved your goal of being able to pick random ls from this distribution, you have also achieved nirvana.
A little more work done
I'll help a little bit more. I tried doing what I said about for y <= .5, but the symbolic algebra system I was using wasn't able to do the inversion (some other system might be able to). However, then I decided to try using the equation for .5 < y <= 1. This turns out to be much easier. If I change l to x in f_s(l) I get
y / n / (1 - y) * (x / n)^((2 * y - 1) / (1 - y))
Integrating this over x from 0 to l I got (using Mathematica's Online Integrator):
(l / n)^(y / (1 - y))
It doesn't get much nicer than that with this sort of thing. If I set this equal to z and solve for l I get:
l = n * z^(1 / y - 1) for .5 < y <= 1
One quick check is for y = 1. In this case, we get l = n no matter what z is. So far so good. Now, you just generate z (a random number between 0 and 1) and you get an l that is distributed as you desired for .5 < y <= 1. But wait, looking at the graph on page 7 you notice that the probability distribution function is symmetric. That means that we can use the above result to find the value for 0 < y <= .5. We just change l -> n-l and y -> 1-y and get
n - l = n * z^(1 / (1 - y) - 1)
l = n * (1 - z^(1 / (1 - y) - 1)) for 0 < y <= .5
Anyway, that should solve your problem unless I made some error somewhere. Good luck.
Given that for any values l, y, n as described, the terms you call p1 and p2 are both in [0,1) and exp is in [1,..) making pow(p2, exp) also in [0,1) thus I don't see how you'd ever get an output with the range [0,n)