Background Information:
I am writing a program where I am supposed to graph a data set, which is stored in the following file (input file), called "names.txt", using the class DrawingPanel.
This java file allows you to create a panel of a given size, and draw shapes and lines in this panel. The problems I am encountering with my program are unrelated to the specifications and understandings of the class methods.
The data set that I am supposed to graph using this java file is configured in the following way:
[name] [gender] [ranking] [ranking] [ranking] [ranking]....etc for a total of 14 rankings.
Example:
Jane F 98 108 128 116 48 55 47 39 67 202 312 345 436 384
General goal of program:
The user is supposed to input the name and gender they would like to see graphed. The data is then graphed, rankings representing the y axis and time representing the x axis.
My main problem is drawing the lines that compose the graph. The lines of these graphs are drawn with the method:
g.drawLine(x1, y1, x2, y2)
with g representing a Graphics object. Two points are necessary as two endpoints are necessary to create a line.
This for loop is responsible for printing the lines.
for (int j = 0; j < SECTION_WIDTH * DECADES; j += SECTION_WIDTH){
g.setColor(Color.BLACK);
g.drawLine(j, yCoordinate, i + intervalIncrease, yCoordinate2);
g.setColor(Color.RED);
intervalIncrease += SECTION_WIDTH;
g.drawString(intervalLabel, intervalIncrease , yCoordinate);
}
}
SUMMARY OF PROBLEM: The for loop above is the cause of the bugs in my program. The problem is the incorrect printing of the lines that compose the graph. This is caused by incorrect for loop iterations and conditions, which are complicated by the awkward location of the for loop and the fact that two calls of everything are necessary to correctly run the program. Despite guessing-and-checking for several hours, I cannot figure out how to manipulate the loops to make it work.
It is complicated because the drawString method is dependent on the drawLine method variables.
I've tried implementing this into my code but it hasn't worked.
Specific Requirements and Additional Information on DrawingPanel.java:
Please see this (Specification) for specifications on what the graph should look like and other requirements.
An image of what the graph should look like is also provided below.
public static void drawRanks(String line, Graphics g){
int yearIncrease = 0;
int intervalIncrease = 0;
System.out.println(line);
Scanner lineScan = new Scanner(line);
String name = lineScan.next();
String gender = lineScan.next();
for(int i = 0; i < DECADES/2; i++) {
g.setColor(Color.RED);
int rank = lineScan.nextInt();
int rank2 = lineScan.nextInt();
int yCoordinate = rank/2 + 25;
int yCoordinate2 = rank2/2 + 25;
String intervalLabel = name + " " + gender + " " + String.valueOf(rank);
String intervalLabel2 = name + " " + gender + " " + String.valueOf(rank2);
if (rank == 0){
yCoordinate = 525;
}
if (rank2 == 0){
yCoordinate2 = 525;
}
for (int j = 0; j < SECTION_WIDTH * DECADES; j += SECTION_WIDTH){
g.setColor(Color.BLACK);
g.drawLine(j, yCoordinate, i + intervalIncrease, yCoordinate2);
g.setColor(Color.RED);
intervalIncrease += SECTION_WIDTH;
g.drawString(intervalLabel, intervalIncrease , yCoordinate);
}
}
for(int j = 0; j < DECADES; j++) {
g.setColor(Color.BLACK);
g.drawString(String.valueOf(STARTING_YEAR + yearIncrease), SECTION_WIDTH * j, 550);
yearIncrease += 10;
}
for (int k = DECADES * SECTION_WIDTH; k >= 0; k -= SECTION_WIDTH){
g.drawLine(k, 0, k, 550);
}
}
Image of graph produced:
Expected graph:
Please feel free to comment if my explanation is unclear.
Here is where the DrawingPanel class is located, for compilation purposes, if necessary.
DrawingPanel.java
Here is an API for the methods:
DrawingPanel name = new DrawingPanel(width, height);
Graphics g - name.getGraphics();
panel.setBackground(color); - sets panel's background color
g.setColor(color); - sets Graphics pen color (like dipping a brush in paint)
g.drawLine(x1, y2, x2, y2); - a line from points (x1, y1) to (x2, y2)
g.drawString(text, x, y); - the given text with its lower-left corner at (x, y)
Your general idea, that you have to read the ranks in pairs, is wrong. What you have to do is draw a line from the last rank's coordinates to the current rank's coordinates. Here is my version of your method (mind you, I don't have your whole environment so I can't actually test this):
public static void drawRanks(String line, Graphics g){
int yearIncrease = 0;
System.out.println(line);
Scanner lineScan = new Scanner(line);
String name = lineScan.next();
String gender = lineScan.next();
int lastRank = lineScan.nextInt();
int lastY = lastRank/2 + 25;
if ( lastRank == 0 ) {
lastY = 525;
}
int lastX = 0;
while ( lineScan.hasNextInt() ) {
int rank = lineScan.nextInt();
int y = rank/2 + 25;
if (rank == 0){
y = 525;
}
int x = lastX + SECTION_WIDTH;
String intervalLabel = name + " " + gender + " " + String.valueOf(lastRank);
g.setColor(Color.RED);
g.drawLine(lastX, lastY, x, y);
g.drawString(intervalLabel,lastX,lastY);
lastX = x;
lastY = y;
lastRank = rank;
}
g.drawString(intervalLabel,lastX,lastY);
for(int j = 0; j < DECADES; j++) {
g.setColor(Color.BLACK);
g.drawString(String.valueOf(STARTING_YEAR + yearIncrease), SECTION_WIDTH * j, 550);
yearIncrease += 10;
}
for (int k = DECADES * SECTION_WIDTH; k >= 0; k -= SECTION_WIDTH){
g.drawLine(k, 0, k, 550);
}
}
So at first you read the name and gender, and then you read the first rank, to give you initial coordinates.
Then, in every iteration of the loop, you read just one rank. You draw the line from your previous rank to your new rank. You draw the label that belongs to the previous rank at the previous rank's coordinates. And then you save your current x,y and rank to the lastX, lastY and lastRank respectively, so that you can rely on them in the next iteration.
After the loop is done, you still have one label you haven't drawn so you draw that, and then you go on to draw the black lines (I haven't looked into the correctess of your code there, just left it as is).
Related
I am doing this in processing which is essentially java and I have never attempted anything like this before. Can't find any examples of collision detection using arrays to map the pixels.
I am not really trying to make them realistic collisions. I was thinking it would have the same response as if it hit a wall which is just for it to change directions in whatever axis is appropriate for the wall it hit.
I have tried checking if the x and y position are the same but can't seem to make that work. I'd appreciate any input on this.
import java.util.Arrays;
int numOfParticles = 10;
float[] x = new float[numOfParticles]; //initial position of y only matters
float[] px = new float[numOfParticles];
float[] y = new float[numOfParticles];
float[] py = new float[numOfParticles];
int speed = 10;//inversly related to speed
float[] xIncrement = new float[numOfParticles]; //the ratio of increments determines the pattern
float[] yIncrement = new float[numOfParticles]; // it is the slope of the line
//float xIncrement = 10/speed; //the ratio of increments determines the pattern
//float yIncrement = 11/speed; // it is the slope of the line
color currentColor;
int alpha = 100;//range of 0-255
//radius of ball
int radius = 1;
//thickness of line behind ball
int thickness = 5;
int rateOfColor = 5; //this is inversely related to rate but also changes the range of colors
int maxColor = 255;
int minColor = 0;
void setup(){
size(500,500);
background(0);
colorMode(HSB);
strokeWeight(thickness);
frameRate(60);
//initialize particles
for(int i = 0;i<numOfParticles;i++){
xIncrement[i] = random(0,100)/speed; //the ratio of increments determines the pattern
yIncrement[i] = random(0,100)/speed; // it is the slope of the line
x[i] = random(0,width);
px[i] = x[i];
y[i] = random(0,height);
py[i] = y[i];
}
//you can either initialize all of them individually or do a random one
//x[0] = 0;
//px[0] = x[0];
//y[0] = 450;
//py[0] = y[0];
//x[1] = width;
//px[1] = x[1];
//y[1] = 450;
//py[1] = y[1];
}
void draw(){
background(0); //comment out for criss cross
for(int i = 0; i < numOfParticles; i++){
particle(i);
}
}
void particle(int particleNum){
currentColor = color(minColor + (x[particleNum]/rateOfColor)%maxColor,255,255,alpha);
stroke(currentColor);
fill(currentColor);
ellipse(x[particleNum],y[particleNum],radius,radius);
line(px[particleNum],py[particleNum],x[particleNum],y[particleNum]);
px[particleNum] = x[particleNum];
py[particleNum] = y[particleNum];
y[particleNum]+= yIncrement[particleNum];
x[particleNum]+= xIncrement[particleNum];
if(x[particleNum] > width + 1 || x[particleNum] < 0){
x[particleNum] -= 2*xIncrement[particleNum];
xIncrement[particleNum]*=-1;
}
if( y[particleNum] > height + 1 || y[particleNum] < 0){
y[particleNum] -= 2*yIncrement[particleNum];
yIncrement[particleNum]*=-1;
}
//if(Arrays.binarySearch(x,x[particleNum]) >= 0 && Arrays.binarySearch(y,y[particleNum]) >= 0){
// xIncrement[particleNum]*=-1;
// yIncrement[particleNum]*=-1;
// print("*\n");
// stop();
//}
print("x[0] = " + x[0] + "\n");
print("x[1] = " + x[1] + "\n");
print("y[0] = " + y[0] + "\n");
print("y[1] = " + y[1] + "\n");
}
Stack Overflow isn't really designed for general "how do I do this" type questions. It's for specific "I tried X, expected Y, but got Z instead" type questions. But I'll try to help in a general sense:
You need to break your problem down into smaller pieces and then take those pieces on one at a time. Don't worry about the whole particle system. Make it work for a single particle. Do some research on collision detection.
Then if you get stuck, you can post a more specific question along with a MCVE. Good luck.
To improve my knowledge of imaging and get some experience working with the topics, I decided to create a license plate recognition algorithm on the Android platform.
The first step is detection, for which I decided to implement a recent paper titled "A Robust and Efficient Approach to License Plate Detection". The paper presents their idea very well and uses quite simple techniques to achieve detection. Besides some details lacking in the paper, I implemented the bilinear downsampling, converting to gray scale, and the edging + adaptive thresholding as described in Section 3A, 3B.1, and 3B.2.
Unfortunately, I am not getting the output this paper presents in e.g. figure 3 and 6.
The image I use for testing is as follows:
The gray scale (and downsampled) version looks fine (see the bottom of this post for the actual implementation), I used a well-known combination of the RGB components to produce it (paper does not mention how, so I took a guess).
Next is the initial edge detection using the Sobel filter outlined. This produces an image similar to the ones presented in figure 6 of the paper.
And finally, the remove the "weak edges" they apply adaptive thresholding using a 20x20 window. Here is where things go wrong.
As you can see, it does not function properly, even though I am using their stated parameter values. Additionally I have tried:
Changing the beta parameter.
Use a 2d int array instead of Bitmap objects to simplify creating the integral image.
Try a higher Gamma parameter so the initial edge detection allows more "edges".
Change the window to e.g. 10x10.
Yet none of the changes made an improvement; it keeps producing images as the one above. My question is: what am I doing different than what is outlined in the paper? and how can I get the desired output?
Code
The (cleaned) code I use:
public int[][] toGrayscale(Bitmap bmpOriginal) {
int width = bmpOriginal.getWidth();
int height = bmpOriginal.getHeight();
// color information
int A, R, G, B;
int pixel;
int[][] greys = new int[width][height];
// scan through all pixels
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
// get pixel color
pixel = bmpOriginal.getPixel(x, y);
R = Color.red(pixel);
G = Color.green(pixel);
B = Color.blue(pixel);
int gray = (int) (0.2989 * R + 0.5870 * G + 0.1140 * B);
greys[x][y] = gray;
}
}
return greys;
}
The code for edge detection:
private int[][] detectEges(int[][] detectionBitmap) {
int width = detectionBitmap.length;
int height = detectionBitmap[0].length;
int[][] edges = new int[width][height];
// Loop over all pixels in the bitmap
int c1 = 0;
int c2 = 0;
for (int y = 0; y < height; y++) {
for (int x = 2; x < width -2; x++) {
// Calculate d0 for each pixel
int p0 = detectionBitmap[x][y];
int p1 = detectionBitmap[x-1][y];
int p2 = detectionBitmap[x+1][y];
int p3 = detectionBitmap[x-2][y];
int p4 = detectionBitmap[x+2][y];
int d0 = Math.abs(p1 + p2 - 2*p0) + Math.abs(p3 + p4 - 2*p0);
if(d0 >= Gamma) {
c1++;
edges[x][y] = Gamma;
} else {
c2++;
edges[x][y] = d0;
}
}
}
return edges;
}
The code for adaptive thresholding. The SAT implementation is taken from here:
private int[][] AdaptiveThreshold(int[][] detectionBitmap) {
// Create the integral image
processSummedAreaTable(detectionBitmap);
int width = detectionBitmap.length;
int height = detectionBitmap[0].length;
int[][] binaryImage = new int[width][height];
int white = 0;
int black = 0;
int h_w = 20; // The window size
int half = h_w/2;
// Loop over all pixels in the bitmap
for (int y = half; y < height - half; y++) {
for (int x = half; x < width - half; x++) {
// Calculate d0 for each pixel
int sum = 0;
for(int k = -half; k < half - 1; k++) {
for (int j = -half; j < half - 1; j++) {
sum += detectionBitmap[x + k][y + j];
}
}
if(detectionBitmap[x][y] >= (sum / (h_w * h_w)) * Beta) {
binaryImage[x][y] = 255;
white++;
} else {
binaryImage[x][y] = 0;
black++;
}
}
}
return binaryImage;
}
/**
* Process given matrix into its summed area table (in-place)
* O(MN) time, O(1) space
* #param matrix source matrix
*/
private void processSummedAreaTable(int[][] matrix) {
int rowSize = matrix.length;
int colSize = matrix[0].length;
for (int i=0; i<rowSize; i++) {
for (int j=0; j<colSize; j++) {
matrix[i][j] = getVal(i, j, matrix);
}
}
}
/**
* Helper method for processSummedAreaTable
* #param row current row number
* #param col current column number
* #param matrix source matrix
* #return sub-matrix sum
*/
private int getVal (int row, int col, int[][] matrix) {
int leftSum; // sub matrix sum of left matrix
int topSum; // sub matrix sum of top matrix
int topLeftSum; // sub matrix sum of top left matrix
int curr = matrix[row][col]; // current cell value
/* top left value is itself */
if (row == 0 && col == 0) {
return curr;
}
/* top row */
else if (row == 0) {
leftSum = matrix[row][col - 1];
return curr + leftSum;
}
/* left-most column */
if (col == 0) {
topSum = matrix[row - 1][col];
return curr + topSum;
}
else {
leftSum = matrix[row][col - 1];
topSum = matrix[row - 1][col];
topLeftSum = matrix[row - 1][col - 1]; // overlap between leftSum and topSum
return curr + leftSum + topSum - topLeftSum;
}
}
Marvin provides an approach to find text regions. Perhaps it can be a start point for you:
Find Text Regions in Images:
http://marvinproject.sourceforge.net/en/examples/findTextRegions.html
This approach was also used in this question:
How do I separates text region from image in java
Using your image I got this output:
Source Code:
package textRegions;
import static marvin.MarvinPluginCollection.findTextRegions;
import java.awt.Color;
import java.util.List;
import marvin.image.MarvinImage;
import marvin.image.MarvinSegment;
import marvin.io.MarvinImageIO;
public class FindVehiclePlate {
public FindVehiclePlate() {
MarvinImage image = MarvinImageIO.loadImage("./res/vehicle.jpg");
image = findText(image, 30, 20, 100, 170);
MarvinImageIO.saveImage(image, "./res/vehicle_out.png");
}
public MarvinImage findText(MarvinImage image, int maxWhiteSpace, int maxFontLineWidth, int minTextWidth, int grayScaleThreshold){
List<MarvinSegment> segments = findTextRegions(image, maxWhiteSpace, maxFontLineWidth, minTextWidth, grayScaleThreshold);
for(MarvinSegment s:segments){
if(s.height >= 10){
s.y1-=20;
s.y2+=20;
image.drawRect(s.x1, s.y1, s.x2-s.x1, s.y2-s.y1, Color.red);
image.drawRect(s.x1+1, s.y1+1, (s.x2-s.x1)-2, (s.y2-s.y1)-2, Color.red);
image.drawRect(s.x1+2, s.y1+2, (s.x2-s.x1)-4, (s.y2-s.y1)-4, Color.red);
}
}
return image;
}
public static void main(String[] args) {
new FindVehiclePlate();
}
}
So, I have been working on this program for drawing a star from a circle created using g.fillPolygon(int, int, int). I was intially having issues with creating an entire circle, but changed double angle = (i * 360) to (i * 720) to fix that (may be a band-aid fix, not sure yet). Now I'm in the process of attempting to connect all the points together (as shown in the "Target Output" section).
Note: I believe that the labeling of the points shown in the modification section was not done wih Java.
My Code: (Where I'm at right now)
import java.awt.*;
public class StarSampler {
public static void main(String[] args)
{
DrawingPanel panel = new DrawingPanel(500, 500);
Graphics2D g = panel.getGraphics();
g.setColor(Color.YELLOW);
fillStar(g, 250, 250, 150, 50, .7);
}
public static void fillStar(Graphics2D g, int ctrX, int ctrY, int radius, int nPoints, double spikiness)
{
double xDouble[] = new double[2*nPoints];
double yDouble[] = new double[2*nPoints];
int xPoint[] = new int[100];
int yPoint[] = new int[100];
for (int i = 0; i < 2*nPoints; i++)
{
double iRadius = (i % 2 == 0) ? radius : (radius * spikiness);
double angle = (i * 720.0) / (2*nPoints);
xDouble[i] = ctrX + iRadius * Math.cos(Math.toRadians(angle));
yDouble[i] = ctrY + iRadius * Math.sin(Math.toRadians(angle));
for (int j = 0; j < nPoints; j++) // Casts for ints and doubles
{
xPoint[j] = (int) xDouble[j];
yPoint[j] = (int) yDouble[j];
}
}
g.fillPolygon(xPoint, yPoint, nPoints); // Creates polygon but
}
}
My Code's Output:
Target Output (What I'm generally aiming for, not both together):
I'm learning Java out of The Art & Science of Java by Roberts (Stanford's CS106a text). I'm using NetBeans as my IDE.
Chapter 4, exercise 14 asks you to expand on a Checkerboard program introduced earlier. Specifically, it asks you to center the checkerboard and draw a set of red & white checks corresponding to the initial state of the game.
I've accomplished as much as requested, but have two issues-
The board is not completely centered in the window. It is closer to the left side of the window than the right side. I am not sure how to center it more. Am I doing this right? Is there a setting in NetBeans I can/should change?
The checkers are supposed to take up a large portion of the tiles they sit on. I assigned the size of my checkers to be dependent on the size of tiles so that the setup would be simple and proportionate. Is there a better way to do this to make the checkers bigger?
import acm.graphics.*;
import acm.program.*;
import java.awt.*;
public class Checkerboard extends GraphicsProgram{
public void run(){
double sqSize = (double)getHeight() / N_ROWS;
for (int i = 0; i < N_ROWS; i++){
for(int j = 0; j < N_COLUMNS; j++){
double x = ((j * sqSize) + (getWidth() / N_COLUMNS)); //centers square??
double y = (i * sqSize);
GRect sq = new GRect( x, y, sqSize, sqSize );
sq.setFilled((i + j) % 2 != 0);
sq.setFillColor(Color.GRAY);
add(sq);
double circleCoord = (sqSize * .33);
double xx = ((j * sqSize) + (getWidth() / N_COLUMNS) + circleCoord);
double yy = ((i * sqSize) + circleCoord);
if((i + j) % 2 != 0 && i < 3 ){
GOval red = new GOval( xx, yy, circleCoord, circleCoord);
red.setFilled(true);
red.setFillColor(Color.RED);
add(red);
} else if((i + j) % 2 != 0 && i > 4 ){
GOval black = new GOval( xx, yy, circleCoord, circleCoord);
black.setFilled(true);
black.setFillColor(Color.BLACK);
add(black);
}
}
}
}
private static final int N_ROWS = 8;
private static final int N_COLUMNS = 8;
}
For 1. The center of the board should be in the center of the width, too. So we know that
The left edge of tile N_COLUMNS/2 = getWidth()/2 e.g. tile 4 in 0 indexing has its left edge in the center
And every tile left or right of that will have a movement of sqSize, so:
double x = getWidth()/2 + (j-N_COLUMNS/2)*sqSize
I am creating objects on a line in a window created by this piece of code:
void createTurtles() {
int nrTurtles = Keyboard.nextInt("Set amount of turtles: ");
w = new GraphicsWindow(500, 300);
drawLinez();
for (int k = 1; k <= nrTurtles; k++) {
Turtle t = new Turtle(w, 50, 50 + k*10);
t.right(90);
t.setSpeed(100);
t.penDown();
turtles.add(t);
}
}
This codeline:
Turtle t = new Turtle(w, 50, 50 + k*10);
Creates one turtle at the time. Right now i have set that the turtles will have the Y coordinat of 50, and the X coordinat of 50+k*10. This is because the line starts at the X coordinat of 50 and stops at the X coordinat of 250.
Now what i want is, based on the nr of turtles created (user inputs this), i want the turtles to be spread on this line evenly. How to do it? It has do to with the line that i wrote and maybe the k value or the 10.
The line is illustrated in the picture (see link below), its the red line, that the number of turtles are created at.
Devide the height - 100 of the window by the number of turtles and you will have your distanceBetweenTurles:
int nrTurtles = Keyboard.nextInt("Set amount of turtles: ");
int height = 300;
w = new GraphicsWindow(500, height);
drawLinez();
double distanceBetweenTurles = (height - 100.0) / nrTurtles;
for (int k = 1; k <= nrTurtles; k++) {
Turtle t = new Turtle(w, 50, 50 + (int) (k * distanceBetweenTurtles));
t.right(90);
t.setSpeed(100);
t.penDown();
turtles.add(t);
}