This question was already asked, but I still don't get it. I obtain a homography matrix by calling cv::findHomography from a set of points. I need to check whether it's relevant or not.The proposed method is to calculate maximum reprojection error for inliers and compare it with a threshold. But after such filtration I keep getting insane transformations with object bounding box transforming to almost a straight line or some strange non-convex quadrangle, with self-intersections etc.What constraints can be used to check if the homography matrix itself is adequate?
Your question is mathematical. Given a matrix of 3x3 decide whether it represents a good rigid transformation.
It is hard to define what is "good" but here are some clues that can help you
Homography should preserve the direction of polygonal points. Design a simple test. points (0,0), (imwidth,0), (width,height), (0,height) represent a quadrilateral with clockwise arranged points. Apply homography on those points and see if they are still clockwise arranged if they become counter clockwise your homography is flipping (mirroring) the image which is sometimes still ok. But if your points are out of order than you have a "bad homography"
The homography doesn't change the scale of the object too much. For example if you expect it to shrink or enlarge the image by a factor of up to X, just check this rule. Transform the 4 points (0,0), (imwidth,0), (width-1,height), (0,height) with homography and calculate the area of the quadrilateral (opencv method of calculating area of polygon) if the ratio of areas is too big (or too small), you probably have an error.
Good homography is usually uses low values of perspectivity. Typically if the size of the image is ~1000x1000 pixels those values should be ~0.005-0.001. High perspectivity will cause enormous distortions which are probably an error. If you don't know where those values are located read my post:
trying to understand the Affine Transform
. It explains the affine transform math and the other 2 values are perspective parameters.
I think that if you check the above 3 condition (condition 2 is the most important) you will be able to detect most of the problems.
Good luck
Edit: This answer is irrelevant to the question, but the discussion may be helpful for someone who tries to use the matching results for recognition like I did!
This might help someone:
Point2f[] objCorners = { new Point2f(0, 0),
new Point2f(img1.Cols, 0),
new Point2f(img1.Cols, img1.Rows),
new Point2f(0, img1.Rows) };
Point2d[] sceneCorners = MyPerspectiveTransform3(objCorners, homography);
double marginH = img2.Width * 0.1d;
double marginV = img2.Height * 0.1d;
bool homographyOK = isInside(-marginH, -marginV, img2.Width + marginH, img2.Height + marginV, sceneCorners);
if (homographyOK)
for (int i = 1; i < sceneCorners.Length; i++)
if (sceneCorners[i - 1].DistanceTo(sceneCorners[i]) < 1)
{
homographyOK = false;
break;
}
if (homographyOK)
homographyOK = isConvex(sceneCorners);
if (homographyOK)
homographyOK = minAngleCheck(sceneCorners, 20d);
private static bool isInside(dynamic minX, dynamic minY, dynamic maxX, dynamic maxY, dynamic coors)
{
foreach (var c in coors)
if ((c.X < minX) || (c.Y < minY) || (c.X > maxX) || (c.Y > maxY))
return false;
return true;
}
private static bool isLeft(dynamic a, dynamic b, dynamic c)
{
return ((b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X)) > 0;
}
private static bool isConvex<T>(IEnumerable<T> points)
{
var lst = points.ToList();
if (lst.Count > 2)
{
bool left = isLeft(lst[0], lst[1], lst[2]);
lst.Add(lst.First());
for (int i = 3; i < lst.Count; i++)
if (isLeft(lst[i - 2], lst[i - 1], lst[i]) != left)
return false;
return true;
}
else
return false;
}
private static bool minAngleCheck<T>(IEnumerable<T> points, double angle_InDegrees)
{
//20d * Math.PI / 180d
var lst = points.ToList();
if (lst.Count > 2)
{
lst.Add(lst.First());
for (int i = 2; i < lst.Count; i++)
{
double a1 = angleInDegrees(lst[i - 2], lst[i-1]);
double a2 = angleInDegrees(lst[i], lst[i - 1]);
double d = Math.Abs(a1 - a2) % 180d;
if ((d < angle_InDegrees) || ((180d - d) < angle_InDegrees))
return false;
}
return true;
}
else
return false;
}
private static double angleInDegrees(dynamic v1, dynamic v2)
{
return (radianToDegree(Math.Atan2(v1.Y - v2.Y, v1.X - v2.X))) % 360d;
}
private static double radianToDegree(double radian)
{
var degree = radian * (180d / Math.PI);
if (degree < 0d)
degree = 360d + degree;
return degree;
}
static Point2d[] MyPerspectiveTransform3(Point2f[] yourData, Mat transformationMatrix)
{
Point2f[] ret = Cv2.PerspectiveTransform(yourData, transformationMatrix);
return ret.Select(point2fToPoint2d).ToArray();
}
Related
Question is, how to clusterize pairs of some units by their angle? Problem is that, kmeans operates on the notion of Euclidean space distance and does not know about periodic nature of angles. So to make it work, one needs to translate the angle to Euclidean space but hold the following true:
close angles are close values in Euclidean space;
far angles are far in Euclidean space.
Which means, that, 90 and -90 are distant values, 180 and -180 is the same, 170 and -170 are close (angles come from left up and to right: 0 - +180 and from left down to the right: 0 - -180)
I tried to use various sin() functions but they all have issues mentioned in points 1 and 2. Most perspective one is sin(x * 0.5f) but also having the problem that 180 and -180 are distant values in Euclidean space.
The solution I found is to translate angles to points on circle and feed them into kmeans. This way we make it to compare distances between points and this works perfectly.
Important thing to mention. Kmeans #eps in termination criterion is expressed in terms of units of samples that you feed to kmeans. In our example maximal distant points have dist 200 units (2 * radius). This means that having 1.0f is totally fine. If you use cv::normalize(samples, samples, 0.0f, 1.0f) for your samples before calling kmeans(), adjust your #eps appropriately. Something like eps=0.01f plays better here.
Enjoy! Hope this helps someone.
static cv::Point2f angleToPointOnCircle(float angle, float radius, cv::Point2f origin /* center */)
{
float x = radius * cosf(angle * M_PI / 180.0f) + origin.x;
float y = radius * sinf(angle * M_PI / 180.0f) + origin.y;
return cv::Point2f(x, y);
}
static std::vector<std::pair<size_t, int> > biggestKmeansGroup(const std::vector<int> &labels, int count)
{
std::vector<std::pair<size_t, int> > indices;
std::map<int, size_t> l2cm;
for (int i = 0; i < labels.size(); ++i)
l2cm[labels[i]]++;
std::vector<std::pair<size_t, int> > c2lm;
for (std::map<int, size_t>::iterator it = l2cm.begin(); it != l2cm.end(); it++)
c2lm.push_back(std::make_pair(it->second, it->first)); // count, group
std::sort(c2lm.begin(), c2lm.end(), cmp_pair_first_reverse);
for (int i = 0; i < c2lm.size() && count-- > 0; i++)
indices.push_back(c2lm[i]);
return indices;
}
static void sortByAngle(std::vector<boost::shared_ptr<Pair> > &group,
std::vector<boost::shared_ptr<Pair> > &result)
{
std::vector<int> labels;
cv::Mat samples;
/* Radius is not so important here. */
for (int i = 0; i < group.size(); i++)
samples.push_back(angleToPointOnCircle(group[i]->angle, 100, cv::Point2f(0, 0)));
/* 90 degrees per group. May be less if you need it. */
static int PAIR_MAX_FINE_GROUPS = 4;
int groupNr = std::max(std::min((int)group.size(), PAIR_MAX_FINE_GROUPS), 1);
assert(group.size() >= groupNr);
cv::kmeans(samples.reshape(1, (int)group.size()), groupNr, labels,
cvTermCriteria(CV_TERMCRIT_EPS/* | CV_TERMCRIT_ITER*/, 30, 1.0f),
100, cv::KMEANS_RANDOM_CENTERS);
std::vector<std::pair<size_t, int> > biggest = biggestKmeansGroup(labels, groupNr);
for (int g = 0; g < biggest.size(); g++) {
for (int i = 0; i < group.size(); i++) {
if (labels[i] == biggest[g].second)
result.push_back(group[i]);
}
}
}
Recently I had the idea to make a pendulum out of points using Processing, and with a little learning I solved it easily:
int contador = 0;
int curvatura = 2;
float pendulo;
void setup(){
size(300,300);
}
void draw(){
background(100);
contador = (contador + 1) % 360; //"CONTADOR" GOES FROM 0 TO 359
pendulo = sin(radians(contador))*curvatura; //"PENDULO" EQUALS THE SIN OF CONTADOR, SO IT GOES FROM 1 TO -1 REPEATEDLY, THEN IS MULTIPLIED TO EMPHASIZE OR REDUCE THE CURVATURE OF THE LINE.
tallo(width/2,height/3);
println(pendulo);
}
void tallo (int x, int y){ //THE FUNTION TO DRAW THE DOTTED LINE
pushMatrix();
translate(x,y);
float _y = 0.0;
for(int i = 0; i < 25; i++){ //CREATES THE POINTS SEQUENCE.
ellipse(0,0,5,5);
_y+=5;
rotate(radians(pendulo)); //ROTATE THEM ON EACH ITERATION, THIS MAKES THE SPIRAL.
}
popMatrix();
}
So, in a brief, what I did was a function that changed every point position with the rotate fuction, and then I just had to draw the ellipses in the origin coordinates as that is the real thing that changes position and creates the pendulum ilussion.
[capture example, I just need 2 more points if you are so gentile :)]
[capture example]
[capture example]
Everything was OK that far. The problem appeared when I tried to replace the ellipses for a path made of vertices. The problem is obvious: the path is never (visually) made because all vertices would be 0,0 as they move along with the zero coordinates.
So, in order to make the path possible, I need the absolute values for each vertex; and there's the question: How do I get them?
What I know I have to do is to remove the transform functions, create the variables for the X and Y position and update them inside the for, but then what? That's why I cleared this is a maths issue, which operation I have to add in the X and Y variables in order to make the path and its curvature possible?
void tallo (int x, int y){
pushMatrix();
translate(x,y);
//NOW WE START WITH THE CHANGES. LET'S DECLARE THE VARIABLES FOR THE COORDINATES
float _x = 0.0;
float _y = 0.0;
beginShape();
for(int i = 0; i < 25; i++){ //CREATES THE DOTS.
vertex(_x,_y); //CHANGING TO VERTICES AND CALLING THE NEW VARIABLES, OK.
//rotate(radians(pendulo)); <--- HERE IS MY PROBLEM. HOW DO I CONVERT THIS INTO X AND Y COORDINATES?
//_x = _x + ????;
_y = _y + 5 /* + ???? */;
}
endShape();
popMatrix();
}
We need to have in mind that pendulo's x and y values changes in each iteration of the for, it doesn't has to add the same quantity each time. The addition must be progressive. Otherwise, we would see a straight line rotating instead of a curve accentuating its curvature (if you increase curvatura's value to a number greater than 20, you will notice the spiral)
So, rotating the coordinates was a great solution to it, now it's kind of a muddle to think the mathematical solution to the x and y coordinates for the spiral, my secondary's knowledges aren't enough. I know I have to create another variable inside the for in order to do this progression, but what operation should it have?
I would be really glad to know, maths
You could use simple trigonometry. You know the angle and the hypotenuse, so you use cos to get the relative x position, and sin to the y. The position would be relative to the central point.
But before i explain in detail and draw some explanations, let me propose another solution: PVectors
void setup() {
size(400,400);
frameRate(60);
center = new PVector(width/2, height/3); //defined here because width and height only are set after size()
}
void draw() {
background(255);
fill(0);
stroke(0);
angle = arc_magn*sin( (float) frameCount/60 );
draw_pendulum( center );
}
PVector center;
float angle = 0;
float arc_magn = HALF_PI;
float wire_length = 150;
float rotation_angle = PI/20 /60 ; //we divide it by 60 so the first part is the rotation in one second
void draw_pendulum(PVector origin){
PVector temp_vect = PVector.fromAngle( angle + HALF_PI);
temp_vect.setMag(wire_length);
PVector final_pos = new PVector(origin.x+temp_vect.x, origin.y+temp_vect.y );
ellipse( final_pos.x, final_pos.y, 40, 40);
line(origin.x, origin.y, final_pos.x, final_pos.y);
}
You use PVector class static method fromAngle( float angle ) that returns a unity vector of the given angle, then use .setMag() to define it's length.
Those PVector methods will take care of the trigonometry for you.
If you still want to know the math behind it, i can make another example.
As my tile says that I want to get random number for origin (X-Axis & y-Axis) so in my whole screen in iPad landscape I have 1 rectangle, I want to get random number for origin which out of this rectangle, so obiously I want to get random number for X-Axis between max and min and same as for Y-Axis.
I tried with following answers but not helpful for me.
Generate Random Numbers Between Two Numbers in Objective-C
Generate a random float between 0 and 1
Generate random number in range in iOS?
For more clear see below image
In above image I just want to find random number (for origin) of GREEN screen. How can I achieve it ?
Edited
I had tried.
int randNum = rand() % ([max intValue] - [min intValue]) + [min intValue];
Same for both X-Axis & y-Axis.
If the blue exclusion rectangle is not "too large" compared to the green screen rectangle
then the easiest solution is to
create a random point inside the green rectangle,
check if the point lies inside the blue rectangle, and
repeat the process if necessary.
That would look like:
CGRect greenRect = ...;
CGRect blueRect = ...;
CGPoint p;
do {
p = CGPointMake(greenRect.origin.x + arc4random_uniform(greenRect.size.width),
greenRect.origin.y + arc4random_uniform(greenRect.size.height));
} while (CGRectContainsPoint(blueRect, p));
If I remember correctly, the expected number of iterations is G/(G - B), where G is
the area of the green rectangle and B is the area of the blue rectangle.
What if you first determined x within the green rectangle like this:
int randomX = arc4random()%greenRectangle.frame.size.width;
int randomY; // we'll do y later
Then check if this is inside the blue rectangle:
if(randomX < blueRectangle.frame.origin.x && randomX > (blueRectangle.frame.origin.x + blueRectangle.frame.size.width))
{
//in this case we are outside the rectangle with the x component
//so can randomly generate any y like this:
randomY = arc4random()%greenRectangle.frame.size.height;
}
//And if randomX is in the blue rectangle then we can use the space either before or after it:
else
{
//randomly decide if you are going to use the range to the left of blue rectangle or to the right
BOOL shouldPickTopRange = arc4random()%1;
if(shouldPickTopRange)
{
//in this case y can be any point before the start of blue rectangle
randomY = arc4random()%blueRectangle.frame.origin.y;
}
else
{
//in this case y can be any point after the blue rectangle
int minY = blueRectangle.frame.origin.y + blueRectangle.frame.size.height;
int maxY = greenRectangle.frame.size.height;
randomY = arc4random()%(maxY - minY + 1) + minY;
}
}
Then your random point would be:
CGPoint randomPoint = CGPointMake(randomX, randomY);
The only thing missing above is to check if your blue rectangle sits at y = 0 or at the very bottom of green rectangle.
[Apologies I did this with OS X, translation is straightforward]
A non-iterative solution:
- (NSPoint) randomPointIn:(NSRect)greenRect excluding:(NSRect)blueRect
{
// random point on green x-axis
int x = arc4random_uniform(NSWidth(greenRect)) + NSMinX(greenRect);
if (x < NSMinX(blueRect) || x > NSMaxX(blueRect))
{
// to the left or right of the blue, full height available
int y = arc4random_uniform(NSHeight(greenRect)) + NSMinY(greenRect);
return NSMakePoint(x, y);
}
else
{
// within the x-range of the blue, avoid it
int y = arc4random_uniform(NSHeight(greenRect) - NSHeight(blueRect)) + NSMinY(greenRect);
if (y >= NSMinY(blueRect))
{
// not below the blue, step over it
y += NSHeight(blueRect);
}
return NSMakePoint(x, y);
}
}
This picks a random x-coord in the range of green. If that point is outside the range of blue it picks a random y-coord in the range of green; otherwise it reduces the y range by the height of blue, produces a random point, and then increases it if required to avoid blue.
There are other solutions based on picking a uniform random point in the available area (green - blue) and then adjusting, but the complexity isn't worth it I think (I haven't done the stats).
Addendum
OK folk seem concerned over uniformity, so here is the algorithm mentioned in my last paragraph. We're picking an "point" with integer coords so the number of points to pick from is the green area minus the blue area. Pick a point randomly in this range. Now place it into one of the rectangles below, left, right or above the blue:
// convenience
int RectArea(NSRect r) { return (int)NSWidth(r) * (int)NSHeight(r); }
- (NSPoint) randomPointIn:(NSRect)greenRect excluding:(NSRect)blueRect
{
// not we are using "points" with integer coords so the
// bottom left point is 0,0 and the top right (width-1, height-1)
// you can adjust this to suit
// the number of points to pick from is the diff of the areas
int availableArea = RectArea(greenRect) - RectArea(blueRect);
int pointNumber = arc4random_uniform(availableArea);
// now "just" locate pointNumber into the available space
// we consider four rectangles, one each full width above and below the blue
// and one each to the left and right of the blue
int belowArea = NSWidth(greenRect) * (NSMinY(blueRect) - NSMinY(greenRect));
if (pointNumber < belowArea)
{
return NSMakePoint(pointNumber % (int)NSWidth(greenRect) + NSMinX(greenRect),
pointNumber / (int)NSWidth(greenRect) + NSMinY(greenRect));
}
// not below - consider to left
pointNumber -= belowArea;
int leftWidth = NSMinX(blueRect) - NSMinX(greenRect);
int leftArea = NSHeight(blueRect) * leftWidth;
if (pointNumber < leftArea)
{
return NSMakePoint(pointNumber % leftWidth + NSMinX(greenRect),
pointNumber / leftWidth + NSMinY(blueRect));
}
// not left - consider to right
pointNumber -= leftArea;
int rightWidth = NSMaxX(greenRect) - NSMaxX(blueRect);
int rightArea = NSHeight(blueRect) * rightWidth;
if (pointNumber < rightArea)
{
return NSMakePoint(pointNumber % rightWidth + NSMaxX(blueRect),
pointNumber / rightWidth + NSMinY(blueRect));
}
// it must be above
pointNumber -= rightArea;
return NSMakePoint(pointNumber % (int)NSWidth(greenRect) + NSMinX(greenRect),
pointNumber / (int)NSWidth(greenRect) + NSMaxY(blueRect));
}
This is uniform, but whether it is worth it you'll have to decide.
Okay. This was bothering me, so I did the work. It's a lot of source code, but computationally lightweight and probabilistically correct (haven't tested).
With all due respect to #MartinR, I think this is superior insofar as it doesn't loop (consider the case where the contained rect covers a very large portion of the outer rect). And with all due respect to #CRD, it's a pain, but not impossible to get the desired probabilities. Here goes:
// Find a random position in rect, excluding a contained rect called exclude
//
// It looks terrible, but it's just a lot of bookkeeping.
// Divide rect into 8 regions, like a tic-tac-toe board, excluding the center square
// Reading left to right, top to bottom, call these: A,B,C,D, (no E, it's the center) F,G,H,I
// The random point must be in one of these regions, choose by throwing a random dart, using
// cumulative probabilities to choose. The likelihood that the dart will be in regions A-I is
// the ratio of each's area to the total (less the center)
// With a target rect, correctly selected, we can easily pick a random point within it.
+ (CGPoint)pointInRect:(CGRect)rect excluding:(CGRect)exclude {
// find important points in the grid
CGFloat xLeft = CGRectGetMinX(rect);
CGFloat xCenter = CGRectGetMinX(exclude);
CGFloat xRight = CGRectGetMaxX(exclude);
CGFloat widthLeft = exclude.origin.x-CGRectGetMinX(rect);
CGFloat widthCenter = exclude.size.width;
CGFloat widthRight = CGRectGetMaxY(rect)-CGRectGetMaxX(exclude);
CGFloat yTop = CGRectGetMinY(rect);
CGFloat yCenter = exclude.origin.y;
CGFloat yBottom = CGRectGetMaxY(exclude);
CGFloat heightTop = exclude.origin.y-CGRectGetMinY(rect);
CGFloat heightCenter = exclude.size.height;
CGFloat heightBottom = CGRectGetMaxY(rect)-CGRectGetMaxY(exclude);
// compute the eight regions
CGFloat areaA = widthLeft * heightTop;
CGFloat areaB = widthCenter * heightTop;
CGFloat areaC = widthRight * heightTop;
CGFloat areaD = widthLeft * heightCenter;
CGFloat areaF = widthRight * heightCenter;
CGFloat areaG = widthLeft * heightBottom;
CGFloat areaH = widthCenter * heightBottom;
CGFloat areaI = widthRight * heightBottom;
CGFloat areaSum = areaA+areaB+areaC+areaD+areaF+areaG+areaH+areaI;
// compute the normalized probabilities
CGFloat pA = areaA/areaSum;
CGFloat pB = areaB/areaSum;
CGFloat pC = areaC/areaSum;
CGFloat pD = areaD/areaSum;
CGFloat pF = areaF/areaSum;
CGFloat pG = areaG/areaSum;
CGFloat pH = areaH/areaSum;
// compute cumulative probabilities
CGFloat cumB = pA+pB;
CGFloat cumC = cumB+pC;
CGFloat cumD = cumC+pD;
CGFloat cumF = cumD+pF;
CGFloat cumG = cumF+pG;
CGFloat cumH = cumG+pH;
// now pick which region we're in, using cumulatvie probabilities
// whew, maybe we should just use MartinR's loop. No No, we've come too far!
CGFloat dart = uniformRandomUpTo(1.0);
CGRect targetRect;
// top row
if (dart < pA) {
targetRect = CGRectMake(xLeft, yTop, widthLeft, heightTop);
} else if (dart >= pA && dart < cumB) {
targetRect = CGRectMake(xCenter, yTop, widthCenter, heightTop);
} else if (dart >= cumB && dart < cumC) {
targetRect = CGRectMake(xRight, yTop, widthRight, heightTop);
}
// middle row
else if (dart >= cumC && dart < cumD) {
targetRect = CGRectMake(xRight, yCenter, widthRight, heightCenter);
} else if (dart >= cumD && dart < cumF) {
targetRect = CGRectMake(xLeft, yCenter, widthLeft, heightCenter);
}
// bottom row
else if (dart >= cumF && dart < cumG) {
targetRect = CGRectMake(xLeft, yBottom, widthLeft, heightBottom);
} else if (dart >= cumG && dart < cumH) {
targetRect = CGRectMake(xCenter, yBottom, widthCenter, heightBottom);
} else {
targetRect = CGRectMake(xRight, yBottom, widthRight, heightBottom);
}
// yay. pick a point in the target rect
CGFloat x = uniformRandomUpTo(targetRect.size.width) + CGRectGetMinX(targetRect);
CGFloat y = uniformRandomUpTo(targetRect.size.height)+ CGRectGetMinY(targetRect);
return CGPointMake(x, y);
}
float uniformRandomUpTo(float max) {
return max * arc4random_uniform(RAND_MAX) / RAND_MAX;
}
Try this code, Worked for me.
-(CGPoint)randomPointInRect:(CGRect)r
{
CGPoint p = r.origin;
p.x += arc4random_uniform((u_int32_t) CGRectGetWidth(r));
p.y += arc4random_uniform((u_int32_t) CGRectGetHeight(r));
return p;
}
I don't like piling onto answers. However, the provided solutions do not work, so I feel obliged to chime in.
Martin's is fine, and simple... which may be all you need. It does have one major problem though... finding the answer when the inner rectangle dominates the containing rectangle could take quite a long time. If it fits your domain, then always choose the simplest solution that works.
jancakes solution is not uniform, and contains a fair amount of bias.
The second solution provided by dang just plain does not work... because arc4_random takes and returns uint32_t and not a floating point value. Thus, all generated numbers should fall into the first box.
You can address that by using drand48(), but it's not a great number generator, and has bias of its own. Furthermore, if you look at the distribution generated by that method, it has heavy bias that favors the box just to the left of the "inner box."
You can easily test the generation... toss a couple of UIViews in a controller, add a button handler that plots 100000 "random" points and you can see the bias clearly.
So, I hacked up something that is not elegant, but does provide a uniform distribution of random numbers in the larger rectangle that are not in the contained rectangle.
You can surely optimize the code and make it a bit easier to read...
Caveat: Will not work if you have more than 4,294,967,296 total points. There are multiple solutions to this, but this should get you moving in the right direction.
- (CGPoint)randomPointInRect:(CGRect)rect
excludingRect:(CGRect)excludeRect
{
excludeRect = CGRectIntersection(rect, excludeRect);
if (CGRectEqualToRect(excludeRect, CGRectNull)) {
return CGPointZero;
}
CGPoint result;
uint32_t rectWidth = rect.size.width;
uint32_t rectHeight = rect.size.height;
uint32_t rectTotal = rectHeight * rectWidth;
uint32_t excludeWidth = excludeRect.size.width;
uint32_t excludeHeight = excludeRect.size.height;
uint32_t excludeTotal = excludeHeight * excludeWidth;
if (rectTotal == 0) {
return CGPointZero;
}
if (excludeTotal == 0) {
uint32_t r = arc4random_uniform(rectHeight * rectWidth);
result.x = r % rectWidth;
result.y = r /rectWidth;
return result;
}
uint32_t numValidPoints = rectTotal - excludeTotal;
uint32_t r = arc4random_uniform(numValidPoints);
uint32_t numPointsAboveOrBelowExcludedRect =
(rectHeight * excludeWidth) - excludeTotal;
if (r < numPointsAboveOrBelowExcludedRect) {
result.x = (r % excludeWidth) + excludeRect.origin.x;
result.y = r / excludeWidth;
if (result.y >= excludeRect.origin.y) {
result.y += excludeHeight;
}
} else {
r -= numPointsAboveOrBelowExcludedRect;
uint32_t numPointsLeftOfExcludeRect =
rectHeight * excludeRect.origin.x;
if (r < numPointsLeftOfExcludeRect) {
uint32_t rowWidth = excludeRect.origin.x;
result.x = r % rowWidth;
result.y = r / rowWidth;
} else {
r -= numPointsLeftOfExcludeRect;
CGFloat startX =
excludeRect.origin.x + excludeRect.size.width;
uint32_t rowWidth = rectWidth - startX;
result.x = (r % rowWidth) + startX;
result.y = r / rowWidth;
}
}
return result;
}
I need to calculate the standard deviation on an image I have inside a UIImage object.
I know already how to access all pixels of an image, one at a time, so somehow I can do it.
I'm wondering if there is somewhere in the framework a function to perform this in a better and more efficient way... I can't find it so maybe it doensn't exist.
Do anyone know how to do this?
bye
To further expand on my comment above. I would definitely look into using the Accelerate framework, especially depending on the size of your image. If you image is a few hundred pixels by a few hundred. You will have a ton of data to process and Accelerate along with vDSP will make all of that math a lot faster since it processes everything on the GPU. I will look into this a little more, and possibly put some code in a few minutes.
UPDATE
I will post some code to do standard deviation in a single dimension using vDSP, but this could definitely be extended to 2-D
float *imageR = [0.1,0.2,0.3,0.4,...]; // vector of values
int numValues = 100; // number of values in imageR
float mean = 0; // place holder for mean
vDSP_meanv(imageR,1,&mean,numValues); // find the mean of the vector
mean = -1*mean // Invert mean so when we add it is actually subtraction
float *subMeanVec = (float*)calloc(numValues,sizeof(float)); // placeholder vector
vDSP_vsadd(imageR,1,&mean,subMeanVec,1,numValues) // subtract mean from vector
free(imageR); // free memory
float *squared = (float*)calloc(numValues,sizeof(float)); // placeholder for squared vector
vDSP_vsq(subMeanVec,1,squared,1,numValues); // Square vector element by element
free(subMeanVec); // free some memory
float sum = 0; // place holder for sum
vDSP_sve(squared,1,&sum,numValues); sum entire vector
free(squared); // free squared vector
float stdDev = sqrt(sum/numValues); // calculated std deviation
Please explain your query so that can come up with specific reply.
If I am getting you right then you want to calculate standard deviation of RGB of pixel or HSV of color, you can frame your own method of standard deviation for circular quantities in case of HSV and RGB.
We can do this by wrapping the values.
For example: Average of [358, 2] degrees is (358+2)/2=180 degrees.
But this is not correct because its average or mean should be 0 degrees.
So we wrap 358 into -2.
Now the answer is 0.
So you have to apply wrapping and then you can calculate standard deviation from above link.
UPDATE:
Convert RGB to HSV
// r,g,b values are from 0 to 1 // h = [0,360], s = [0,1], v = [0,1]
// if s == 0, then h = -1 (undefined)
void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v )
{
float min, max, delta;
min = MIN( r, MIN(g, b ));
max = MAX( r, MAX(g, b ));
*v = max;
delta = max - min;
if( max != 0 )
*s = delta / max;
else {
// r = g = b = 0
*s = 0;
*h = -1;
return;
}
if( r == max )
*h = ( g - b ) / delta;
else if( g == max )
*h=2+(b-r)/delta;
else
*h=4+(r-g)/delta;
*h *= 60;
if( *h < 0 )
*h += 360;
}
and then calculate standard deviation for hue value by this:
double calcStddev(ArrayList<Double> angles){
double sin = 0;
double cos = 0;
for(int i = 0; i < angles.size(); i++){
sin += Math.sin(angles.get(i) * (Math.PI/180.0));
cos += Math.cos(angles.get(i) * (Math.PI/180.0));
}
sin /= angles.size();
cos /= angles.size();
double stddev = Math.sqrt(-Math.log(sin*sin+cos*cos));
return stddev;
}
I'm doing project in OpenCV on object detection which consists of matching the object in template image with the reference image. Using SIFT algorithm the features get acurately detected and matched but I want a rectagle around the matched features
My algorithm uses the KD-Tree est ean First technique to get the matches
If you want a rectangle around the detected object, here you have code example with exactly that. You just need to draw a rectangle around the homography H.
Hope it helps. Good luck.
I use the following code, adapted from the SURF algoritm in OpenCV (modules/features2d/src/surf.cpp) to extract a surrounding of a keypoint.
Apart from other examples based on rectangles and ROI, this code returns the patch correctly oriented according to the orientation and scale determined by the feature detection algorithm (both available in the KeyPoint struct).
An example of the results of the detection on several different images:
const int PATCH_SZ = 20;
Mat extractKeyPoint(const Mat& image, KeyPoint kp)
{
int x = (int)kp.pt.x;
int y = (int)kp.pt.y;
float size = kp.size;
float angle = kp.angle;
int win_size = (int)((PATCH_SZ+1)*size*1.2f/9.0);
Mat win(win_size, win_size, CV_8UC3);
float descriptor_dir = angle * (CV_PI/180);
float sin_dir = sin(descriptor_dir);
float cos_dir = cos(descriptor_dir);
float win_offset = -(float)(win_size-1)/2;
float start_x = x + win_offset*cos_dir + win_offset*sin_dir;
float start_y = y - win_offset*sin_dir + win_offset*cos_dir;
uchar* WIN = win.data;
uchar* IMG = image.data;
for( int i = 0; i < win_size; i++, start_x += sin_dir, start_y += cos_dir )
{
float pixel_x = start_x;
float pixel_y = start_y;
for( int j = 0; j < win_size; j++, pixel_x += cos_dir, pixel_y -= sin_dir )
{
int x = std::min(std::max(cvRound(pixel_x), 0), image.cols-1);
int y = std::min(std::max(cvRound(pixel_y), 0), image.rows-1);
for (int c=0; c<3; c++) {
WIN[i*win_size*3 + j*3 + c] = IMG[y*image.step1() + x*3 + c];
}
}
}
return win;
}
I am not sure if the scale is entirely OK, but it is taken from the SURF source and the results look relevant to me.