Draw different shape of polygon randomly - java

My program draw 10 polygon triangles in random sizes (width and height). The coordinate (points) of the polygon were generated using Random generator = new Random(). Since the points of the triangle and the width and height were randomly generated, how can I control the number of sizes drawn? e.g. there are 10 polygon in total, but I wanted to limit the number of different sizes e.g. only 2 or 4 different sizes.
for(int i = 0; i < 10; i++) {
double xWidth = generator.nextDouble() * 50.0 + 20.0; // range width of triangle
double yHeight = generator.nextDouble() * 50.0 + 20.0; // range height of triangle
xCoord[0] = generator.nextInt(300);
yCoord[0] = generator.nextInt(300);
xCoord[1] = (int) (xCoord[0] - xWidth);
xCoord[2] = (int) (xCoord[1] + (xWidth/2));
yCoord[1] = yCoord[0];
yCoord[2] = (int) (yCoord[1] - yHeight);
triangles.add( new Polygon(xCoord,yCoord, 3));
}

Why not just randomly generate 4 shapes and then run a different loop to pick randomly from those four shapes.

This code only generates the tringles, there has to be a .draw() somewhere - you just wrap that in some sort of code that picks one to four triangles - which again will need some sort of randomizer if you want those selected randomly.

int limit = generator.nextInt(4)+1; // [1,4]
for(int i = 0; i < limit; i++) {
//...
}

Related

Beginner JavaFX, tracking a max radius generated randomly?

I have a project where I have to fill a 600x400 window (JavaFX) with 30 random sized circles with no filling. The largest circle must be filled with a translucent red (and if there are multiple large circles with the same radius only one can be filled). I'm able to get all the circles on the screen fine. My problem is getting the largest circle to be red. I haven't been taught arrays which were used in almost all of my many google searches. I cant figure out how exactly to track the largest circle. His hint to us is : "When it comes to keeping track of the largest circle, remember that two reference variables can point to the same Circle object. Maintain a separate Circle reference variable that always points to the largest circle (so far created). You may want to initialize this variable to a circle that has a radius of 0. You can get the radius of a circle using the getRadius method." I created a circle object and a largestCircle object but don't understand how to make the largestCircle object have the highest radius.
This is the code I have so far:
{
Random gen = new Random();
int x = 0;
int y = 0;
int radius = 0;
double largestRadius = Math.max(radius);
Circle largestCircle = null;
Group root = new Group();
//prints out 30 circles
for (int i = 0; i <= 30; i++)
{
Circle circle = new Circle(x, y, radius);
{
radius = gen.nextInt(66) + 10; //generates random radius from 10 to 75
x = gen.nextInt(600 - 2 * radius) + radius;
y = gen.nextInt(400 - 2 * radius) + radius;
}
if (circle.getRadius() == largestRadius)
{
largestCircle = circle;
largestCircle.setFill(Color.rgb(255, 0, 0, 0.3));
}
circle.setFill(null);
circle.setStroke(Color.rgb(gen.nextInt(256), + gen.nextInt(256), gen.nextInt(256)));
circle.setStrokeWidth(3);
root.getChildren().add(circle);
}
after I generate the random circles how to I find the max radius that was generated and set it to largestCircle? the highest radius a circle can be is 75, but sometimes none of the circles have a radius of 75. How do I set the max to be the highest number the program randomly generates?
Any help would be greatly appreciated! Thank you for your time
How about the following.
It has a two fixes.
-1, use > and not == when figuring if current circle is the largest.
-2, change the color of the largest circle at the end, after all the circles have been made... else you might make multiple circles red.
{
Random gen = new Random();
int x = 0;
int y = 0;
int radius = 0;
double largestRadius = Math.max(radius);
Circle largestCircle = null;
Group root = new Group();
//prints out 30 circles
for (int i = 0; i <= 30; i++)
{
Circle circle = new Circle(x, y, radius);
if (circle.getRadius() > largestRadius)
{
largestCircle = circle;
}
{
radius = gen.nextInt(66) + 10; //generates random radius from 10 to 75
x = gen.nextInt(600 - 2 * radius) + radius;
y = gen.nextInt(400 - 2 * radius) + radius;
}
circle.setFill(null);
circle.setStroke(Color.rgb(gen.nextInt(256), + gen.nextInt(256), gen.nextInt(256)));
circle.setStrokeWidth(3);
root.getChildren().add(circle);
}
largestCircle.setFill(Color.rgb(255, 0, 0, 0.3));
It is generally a good idea to initialize any max variable with a small number that is out of scope for your project. In this case, since radius can not be -1, I would do
double largestRadius = -1;
After this, it doesn't matter how big the radius can be, any radius bigger than -1 will change the largestRadius.
It looks to me like you are only missing one part and that is the if the newly created circle has a radius > largestRadius.
if(circle.getRadius() > largestRadius){
largestCircle = circle;
largestRadius = circle.getRadius();
}
After this, you have checked for if the new circle has a radius greater than AND you have checked if the new circle has a radius equal to. Keeping the if statement that you already have, you will always reference the newest circle with the largestRadius.
I would keep the circle objects in an array. Use a double (or whatever number type is appropriate for your random values) to track the high value with a simple comparison (is my current high value less than the new random value? if so, update high value) each time you generate a random value and create a circle of that size.
Once you have your 30 circles in your array simply loop through it until you find the first occurrence of your high value, when you find it make that circle whatever color.
Circle[] myCircles=new Circle[30];
double largestCircle;
for(int i=0;i<30;i++){
// determine your x,y, and radius here
myCircles[i]=new Circle(x,y,radius);
if(radius>largestCircle) largestCircle=radius;
}
Then to loop thru your myCircles and do things with each one
for(int i=0;i<30;i++){
if(myCircles[i].getRadius()==largestCircle){
// make myCircles[i] red here
}
}

How to get pixels inside of a quadrilateral defined by four points in OpenCV?

I'm new to OpenCV, but with a bit of luck and a lot of time I was able to hack together some code that detects individual cells in a chessboard like so:
The image frame is being stored in a Mat and the corners are being stored in a MatOfPoint2f.
Code to show how I'm using the matrices to draw the cells individually:
private void draw(final Mat frame) {
for (int x = 0; x < BOARD_SIZE - 1; x++)
for (int y = 0; y < BOARD_SIZE - 1; y++) {
final int index = x + y * BOARD_SIZE;
final Point topLeft = cornerPoints.get(index);
final Point bottomLeft = cornerPoints.get(index + BOARD_SIZE);
final Point topRight = cornerPoints.get(index + 1);
final Point bottomRight = cornerPoints.get(index + 1 + BOARD_SIZE);
// left line
Imgproc.line(frame, topLeft, bottomLeft, DEBUG_COLOR);
// right line
Imgproc.line(frame, topRight, bottomRight, DEBUG_COLOR);
// top line
Imgproc.line(frame, topLeft, topRight, DEBUG_COLOR);
// bottom line
Imgproc.line(frame, bottomLeft, bottomRight, DEBUG_COLOR);
}
}
How would I use the four points (the corners of the cells) to get the RGB values of the pixels inside of the each quadrilateral?
Create a mask from your vertices. You can use fillPoly for that.
Then iterate over pixels. If pixel(x,y) is valid in your mask, read RGB else continue. Restrict pixel iteration range using your extreme vertices.
Masking works. If you have lots of polygons, or not too much RAM, a point-in-polygon test may be more efficient, especially if you can guarantee that your quadrilaterals are convex. See this reference

Making an Image Concave in Java

I had a quick question, and wondered if anyone had any ideas or libraries I could use for this. I am making a java game, and need to make 2d images concave. The problem is, 1: I don't know how to make an image concave. 2: I need the concave effect to be somewhat of a post process, think Oculus Rift. Everything is normal, but the camera of the player distorts the normal 2d images to look 3d. I am a Sophmore, so I don't know very complex math to accomplish this.
Thanks,
-Blue
If you're not using any 3D libraries or anything like that, just implement it as a simple 2D distortion. It doesn't have to be 100% mathematically correct as long as it looks OK. You can create a couple of arrays to store the distorted texture co-ordinates for your bitmap, which means you can pre-calculate the distortion once (which will be slow but only happens once) and then render multiple times using the pre-calculated values (which will be faster).
Here's a simple function using a power formula to generate a distortion field. There's nothing 3D about it, it just sucks in the center of the image to give a concave look:
int distortionU[][];
int distortionV[][];
public void computeDistortion(int width, int height)
{
// this will be really slow but you only have to call it once:
int halfWidth = width / 2;
int halfHeight = height / 2;
// work out the distance from the center in the corners:
double maxDistance = Math.sqrt((double)((halfWidth * halfWidth) + (halfHeight * halfHeight)));
// allocate arrays to store the distorted co-ordinates:
distortionU = new int[width][height];
distortionV = new int[width][height];
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// work out the distortion at this pixel:
// find distance from the center:
int xDiff = x - halfWidth;
int yDiff = y - halfHeight;
double distance = Math.sqrt((double)((xDiff * xDiff) + (yDiff * yDiff)));
// distort the distance using a power function
double invDistance = 1.0 - (distance / maxDistance);
double distortedDistance = (1.0 - Math.pow(invDistance, 1.7)) * maxDistance;
distortedDistance *= 0.7; // zoom in a little bit to avoid gaps at the edges
// work out how much to multiply xDiff and yDiff by:
double distortionFactor = distortedDistance / distance;
xDiff = (int)((double)xDiff * distortionFactor);
yDiff = (int)((double)yDiff * distortionFactor);
// save the distorted co-ordinates
distortionU[x][y] = halfWidth + xDiff;
distortionV[x][y] = halfHeight + yDiff;
// clamp
if(distortionU[x][y] < 0)
distortionU[x][y] = 0;
if(distortionU[x][y] >= width)
distortionU[x][y] = width - 1;
if(distortionV[x][y] < 0)
distortionV[x][y] = 0;
if(distortionV[x][y] >= height)
distortionV[x][y] = height - 1;
}
}
}
Call it once passing the size of the bitmap that you want to distort. You can play around with the values or use a totally different formula to get the effect you want. Using an exponent less than one for the pow() function should give the image a convex look.
Then when you render your bitmap, or copy it to another bitmap, use the values in distortionU and distortionV to distort your bitmap, e.g.:
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// int pixelColor = bitmap.getPixel(x, y); // gets undistorted value
int pixelColor = bitmap.getPixel(distortionU[x][y], distortionV[x][y]); // gets distorted value
canvas.drawPixel(x + offsetX, y + offsetY, pixelColor);
}
}
I don't know what your actual function for drawing a pixel to the canvas is called, the above is just pseudo-code.

Hough circle detection accuracy very low

I am trying to detect a circular shape from an image which appears to have very good definition. I do realize that part of the circle is missing but from what I've read about the Hough transform it doesn't seem like that should cause the problem I'm experiencing.
Input:
Output:
Code:
// Read the image
Mat src = Highgui.imread("input.png");
// Convert it to gray
Mat src_gray = new Mat();
Imgproc.cvtColor(src, src_gray, Imgproc.COLOR_BGR2GRAY);
// Reduce the noise so we avoid false circle detection
//Imgproc.GaussianBlur( src_gray, src_gray, new Size(9, 9), 2, 2 );
Mat circles = new Mat();
/// Apply the Hough Transform to find the circles
Imgproc.HoughCircles(src_gray, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 1, 160, 25, 0, 0);
// Draw the circles detected
for( int i = 0; i < circles.cols(); i++ ) {
double[] vCircle = circles.get(0, i);
Point center = new Point(vCircle[0], vCircle[1]);
int radius = (int) Math.round(vCircle[2]);
// circle center
Core.circle(src, center, 3, new Scalar(0, 255, 0), -1, 8, 0);
// circle outline
Core.circle(src, center, radius, new Scalar(0, 0, 255), 3, 8, 0);
}
// Save the visualized detection.
String filename = "output.png";
System.out.println(String.format("Writing %s", filename));
Highgui.imwrite(filename, src);
I have Gaussian blur commented out because (counter intuitively) it was greatly increasing the number of equally inaccurate circles found.
Is there anything wrong with my input image that would cause Hough to not work as well as I expect? Are my parameters way off?
EDIT: first answer brought up a good point about the min/max radius hint for Hough. I resisted adding those parameters as the example image in this post is just one of thousands of images all with varying radii from ~20 to almost infinity.
I've adjusted my RANSAC algorithm from this answer: Detect semi-circle in opencv
Idea:
choose randomly 3 points from your binary edge image
create a circle from those 3 points
test how "good" this circle is
if it is better than the previously best found circle in this image, remember
loop 1-4 until some number of iterations reached. then accept the best found circle.
remove that accepted circle from the image
repeat 1-6 until you have found all circles
problems:
at the moment you must know how many circles you want to find in the image
tested only for that one image.
c++ code
result:
code:
inline void getCircle(cv::Point2f& p1,cv::Point2f& p2,cv::Point2f& p3, cv::Point2f& center, float& radius)
{
float x1 = p1.x;
float x2 = p2.x;
float x3 = p3.x;
float y1 = p1.y;
float y2 = p2.y;
float y3 = p3.y;
// PLEASE CHECK FOR TYPOS IN THE FORMULA :)
center.x = (x1*x1+y1*y1)*(y2-y3) + (x2*x2+y2*y2)*(y3-y1) + (x3*x3+y3*y3)*(y1-y2);
center.x /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
center.y = (x1*x1 + y1*y1)*(x3-x2) + (x2*x2+y2*y2)*(x1-x3) + (x3*x3 + y3*y3)*(x2-x1);
center.y /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
radius = sqrt((center.x-x1)*(center.x-x1) + (center.y-y1)*(center.y-y1));
}
std::vector<cv::Point2f> getPointPositions(cv::Mat binaryImage)
{
std::vector<cv::Point2f> pointPositions;
for(unsigned int y=0; y<binaryImage.rows; ++y)
{
//unsigned char* rowPtr = binaryImage.ptr<unsigned char>(y);
for(unsigned int x=0; x<binaryImage.cols; ++x)
{
//if(rowPtr[x] > 0) pointPositions.push_back(cv::Point2i(x,y));
if(binaryImage.at<unsigned char>(y,x) > 0) pointPositions.push_back(cv::Point2f(x,y));
}
}
return pointPositions;
}
float verifyCircle(cv::Mat dt, cv::Point2f center, float radius, std::vector<cv::Point2f> & inlierSet)
{
unsigned int counter = 0;
unsigned int inlier = 0;
float minInlierDist = 2.0f;
float maxInlierDistMax = 100.0f;
float maxInlierDist = radius/25.0f;
if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
if(maxInlierDist>maxInlierDistMax) maxInlierDist = maxInlierDistMax;
// choose samples along the circle and count inlier percentage
for(float t =0; t<2*3.14159265359f; t+= 0.05f)
{
counter++;
float cX = radius*cos(t) + center.x;
float cY = radius*sin(t) + center.y;
if(cX < dt.cols)
if(cX >= 0)
if(cY < dt.rows)
if(cY >= 0)
if(dt.at<float>(cY,cX) < maxInlierDist)
{
inlier++;
inlierSet.push_back(cv::Point2f(cX,cY));
}
}
return (float)inlier/float(counter);
}
float evaluateCircle(cv::Mat dt, cv::Point2f center, float radius)
{
float completeDistance = 0.0f;
int counter = 0;
float maxDist = 1.0f; //TODO: this might depend on the size of the circle!
float minStep = 0.001f;
// choose samples along the circle and count inlier percentage
//HERE IS THE TRICK that no minimum/maximum circle is used, the number of generated points along the circle depends on the radius.
// if this is too slow for you (e.g. too many points created for each circle), increase the step parameter, but only by factor so that it still depends on the radius
// the parameter step depends on the circle size, otherwise small circles will create more inlier on the circle
float step = 2*3.14159265359f / (6.0f * radius);
if(step < minStep) step = minStep; // TODO: find a good value here.
//for(float t =0; t<2*3.14159265359f; t+= 0.05f) // this one which doesnt depend on the radius, is much worse!
for(float t =0; t<2*3.14159265359f; t+= step)
{
float cX = radius*cos(t) + center.x;
float cY = radius*sin(t) + center.y;
if(cX < dt.cols)
if(cX >= 0)
if(cY < dt.rows)
if(cY >= 0)
if(dt.at<float>(cY,cX) <= maxDist)
{
completeDistance += dt.at<float>(cY,cX);
counter++;
}
}
return counter;
}
int main()
{
//RANSAC
cv::Mat color = cv::imread("HoughCirclesAccuracy.png");
// convert to grayscale
cv::Mat gray;
cv::cvtColor(color, gray, CV_RGB2GRAY);
// get binary image
cv::Mat mask = gray > 0;
unsigned int numberOfCirclesToDetect = 2; // TODO: if unknown, you'll have to find some nice criteria to stop finding more (semi-) circles
for(unsigned int j=0; j<numberOfCirclesToDetect; ++j)
{
std::vector<cv::Point2f> edgePositions;
edgePositions = getPointPositions(mask);
std::cout << "number of edge positions: " << edgePositions.size() << std::endl;
// create distance transform to efficiently evaluate distance to nearest edge
cv::Mat dt;
cv::distanceTransform(255-mask, dt,CV_DIST_L1, 3);
unsigned int nIterations = 0;
cv::Point2f bestCircleCenter;
float bestCircleRadius;
//float bestCVal = FLT_MAX;
float bestCVal = -1;
//float minCircleRadius = 20.0f; // TODO: if you have some knowledge about your image you might be able to adjust the minimum circle radius parameter.
float minCircleRadius = 0.0f;
//TODO: implement some more intelligent ransac without fixed number of iterations
for(unsigned int i=0; i<2000; ++i)
{
//RANSAC: randomly choose 3 point and create a circle:
//TODO: choose randomly but more intelligent,
//so that it is more likely to choose three points of a circle.
//For example if there are many small circles, it is unlikely to randomly choose 3 points of the same circle.
unsigned int idx1 = rand()%edgePositions.size();
unsigned int idx2 = rand()%edgePositions.size();
unsigned int idx3 = rand()%edgePositions.size();
// we need 3 different samples:
if(idx1 == idx2) continue;
if(idx1 == idx3) continue;
if(idx3 == idx2) continue;
// create circle from 3 points:
cv::Point2f center; float radius;
getCircle(edgePositions[idx1],edgePositions[idx2],edgePositions[idx3],center,radius);
if(radius < minCircleRadius)continue;
//verify or falsify the circle by inlier counting:
//float cPerc = verifyCircle(dt,center,radius, inlierSet);
float cVal = evaluateCircle(dt,center,radius);
if(cVal > bestCVal)
{
bestCVal = cVal;
bestCircleRadius = radius;
bestCircleCenter = center;
}
++nIterations;
}
std::cout << "current best circle: " << bestCircleCenter << " with radius: " << bestCircleRadius << " and nInlier " << bestCVal << std::endl;
cv::circle(color,bestCircleCenter,bestCircleRadius,cv::Scalar(0,0,255));
//TODO: hold and save the detected circle.
//TODO: instead of overwriting the mask with a drawn circle it might be better to hold and ignore detected circles and dont count new circles which are too close to the old one.
// in this current version the chosen radius to overwrite the mask is fixed and might remove parts of other circles too!
// update mask: remove the detected circle!
cv::circle(mask,bestCircleCenter, bestCircleRadius, 0, 10); // here the radius is fixed which isnt so nice.
}
cv::namedWindow("edges"); cv::imshow("edges", mask);
cv::namedWindow("color"); cv::imshow("color", color);
cv::imwrite("detectedCircles.png", color);
cv::waitKey(-1);
return 0;
}
If you'd set minRadius and maxRadius paramaeters properly, it'd give you good results.
For your image, I tried following parameters.
method - CV_HOUGH_GRADIENT
minDist - 100
dp - 1
param1 - 80
param2 - 10
minRadius - 250
maxRadius - 300
I got the following output
Note: I tried this in C++.

Dividing a range up evenly, allowing for spaces and margins

I have a point array and a panel. getWidth() is based off the panel. Imagine cards in your hand, so HAND_CARD_WIDTH is pretty self explanatory. The purpose of this loop is to set the points evenly across the panel, which it does. However, it allow the cards to go out of the panel, which makes it look very bad. What I want to do is give a small empty margin on both sides of the panel no matter how many cards you have in your hand.
Here's the code
int iteration = 1;
int limiter = getWidth();
int slice = (limiter/(handsize+1));
while (points.size() < handsize) {
int x = slice*(iteration++);
x -= HAND_CARD_WIDTH/2;
points.add(new Point(x, y));
}
Basically I want the leftmost x to be at least 20 and the rightmost to be at most getWidth() - 20 - HAND_CARD_WIDTH. I also want the cards to be evenly spaced... I just can't think of the right equation (getting to this point was sadly a feat..).
Thanks, based on the responses (all 2 of them) heres what I went with:
if((int)points.get(0).getX() < margin){
int boost = Math.abs(margin - (int)points.get(0).getX());
slice = (boost*2)/handsize;
for(Point p : points){
p.x += boost;
boost -= slice;
}
}
Not sure if I understand your layout, but I think it is something like this:
|margin|space|card|space|card|space|margin|
or
|margin|space|card|space|margin|
So, the number of spaces is one more than number of cards and the total width is component width minus margins. componentWidth = numCards x cardWidth + (numCards + 1) x spaceWidth Now it is easy to calculate the space needed which is (componentWidth - numCards x cardWidth) / (numCards + 1) so the left position of a card is leftMargin + spaceWidth x cardNum + cardWidth x (cardNum - 1)
Care must be taken when the space is negative, then you instead must calculate how much the cards must overlap.
Try this:
final int MARGIN = 20;
int availableWidth = getWidth() - 2 * MARGIN - HAND_CARD_WIDTH;
int cardSpaceWidth = availableWidth / handsize;
for (int i = 0; i < handsize; i++) {
int x = MARGIN + ((i + 0.5) * cardSpaceWidth) - HAND_CARD_WIDTH / 2;
points.add(new Point(x, y));
}
So:
calculate the available width by subtracting the margins from total panel width
calculate the space for each card by dividing remaining space by number of cards
for each card, we count its leftmost point by taking the middle of card space and subtracting half the width of the card.
Mind you, with this solution cards still can overlap margins, if HAND_CARD_WIDTH is greater than available space for card.

Categories

Resources