I have a program that generates a .ppm file with a bunch of black dots on it. What I want to do is to draw lines between these dots to build a graph, but instead of using some swing method I want to know if there is a way to do it by directly manipulating the .ppm's matrix.
I assume it would require some nested loops, but how would I identify which positions I need to change to create the line between two dots?
(for those who don't know, a .ppm file is basically a giant matrix with 3 RGB values for each of it's positions, allowing you to draw stuff pixel by pixel)
Single pixel thickness:
void drawline(Color pixels[][], int width, int height,
int x1, int x2, int y1, int y2, Color line)
{
int dx = x1 - x2;
int dy = y1 - y2;
if (dx != 0 || dy != 0)
{
int n = Math.Max(Math.Abs(dx), Math.Abs(dy));
double inv = 1.0 / (double)(n + 1);
double dxdn = (double)dx * inv;
double dydn = (double)dy * inv;
double x = (double)x1, y = (double)y1;
for (int i = 0; i <= n; i++)
{
int xi = (int)x, yi = (int)y;
if (xi >= 0 || xi < width || yi >= 0 || yi < height)
pixels[yi][xi] = line;
x += dxdn; y += dydn;
}
}
}
(NB this is in C# syntax; you may need minor changes to convert it to Java)
Related
I am trying to flip a square image inside a picture with user input variables. After running the code nothing happens at all. In other attempts I was able to make a small thin rectangle appear. How do I fix this?
public void flipHorizontal (int x, int y, int size)
{
int half = size / 2;
int x1 = x - half;
int y1 = y - half;
int x2 = x + half;
int y2 = y + half;
Pixel sourcePixel = getPixel (x1,y1);
Pixel targetPixel = getPixel (x1, y1 + half);
//loop through columns
for (x = x; x < x2; x++)
{
//loop from 0 to before mirror point
for (y = y; y < y2 + half; y++)
{
Color friend = sourcePixel.getColor();
sourcePixel = getPixel (x1,y1);
targetPixel = getPixel (x1, y1 + half);
targetPixel.setColor (friend);
}
}
}
I am trying to find image in an image. I do this for desktop automation. At this moment, I'm trying to be fast, not precise. As such, I have decided to match similar image solely based on the same average color.
If I pick several icons on my desktop, for example:
And I will search for the last one (I'm still wondering what this file is):
You can clearly see what is most likely to be the match:
In different situations, this may not work. However when image size is given, it should be pretty reliable and lightning fast.
I can get a screenshot as BufferedImage object:
MSWindow window = MSWindow.windowFromName("Firefox", false);
BufferedImage img = window.screenshot();
//Or, if I can estimate smaller region for searching:
BufferedImage img2 = window.screenshotCrop(20,20,50,50);
Of course, the image to search image will be loaded from template saved in a file:
BufferedImage img = ImageIO.read(...whatever goes in there, I'm still confused...);
I explained what all I know so that we can focus on the only problem:
Q: How can I get average color on buffered image? How can I get such average color on sub-rectangle of that image?
Speed wins here. In this exceptional case, I consider it more valuable than code readability.
I think that no matter what you do, you are going to have an O(wh) operation, where w is your width and h is your height.
Therefore, I'm going to post this (naive) solution to fulfil the first part of your question as I do not believe there is a faster solution.
/*
* Where bi is your image, (x0,y0) is your upper left coordinate, and (w,h)
* are your width and height respectively
*/
public static Color averageColor(BufferedImage bi, int x0, int y0, int w,
int h) {
int x1 = x0 + w;
int y1 = y0 + h;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
}
}
int num = w * h;
return new Color(sumr / num, sumg / num, sumb / num);
}
There is a constant time method for finding the mean colour of a rectangular section of an image but it requires a linear preprocess. This should be fine in your case. This method can also be used to find the mean value of a rectangular prism in a 3d array or any higher dimensional analog of the problem. I will be using a gray scale example but this can be easily extended to 3 or more channels simply by repeating the process.
Lets say we have a 2 dimensional array of numbers we will call "img".
The first step is to generate a new array of the same dimensions where each element contains the sum of all values in the original image that lie within the rectangle that bounds that element and the top left element of the image.
You can use the following method to construct such an image in linear time:
int width = 1920;
int height = 1080;
//source data
int[] img = GrayScaleScreenCapture();
int[] helperImg = int[width * height]
for(int y = 0; y < height; ++y)
{
for(int x = 0; x < width; ++x)
{
int total = img[y * width + x];
if(x > 0)
{
//Add value from the pixel to the left in helperImg
total += helperImg[y * width + (x - 1)];
}
if(y > 0)
{
//Add value from the pixel above in helperImg
total += helperImg[(y - 1) * width + x];
}
if(x > 0 && y > 0)
{
//Subtract value from the pixel above and to the left in helperImg
total -= helperImg[(y - 1) * width + (x - 1)];
}
helperImg[y * width + x] = total;
}
}
Now we can use helperImg to find the total of all values within a given rectangle of img in constant time:
//Some Rectangle with corners (x0, y0), (x1, y0) , (x0, y1), (x1, y1)
int x0 = 50;
int x1 = 150;
int y0 = 25;
int y1 = 200;
int totalOfRect = helperImg[y1 * width + x1];
if(x0 > 0)
{
totalOfRect -= helperImg[y1 * width + (x0 - 1)];
}
if(y0 > 0)
{
totalOfRect -= helperImg[(y0 - 1) * width + x1];
}
if(x0 > 0 && y0 > 0)
{
totalOfRect += helperImg[(y0 - 1) * width + (x0 - 1)];
}
Finally, we simply divide totalOfRect by the area of the rectangle to get the mean value:
int rWidth = x1 - x0 + 1;
int rheight = y1 - y0 + 1;
int meanOfRect = totalOfRect / (rWidth * rHeight);
Here's a version based on k_g's answer for a full BufferedImage with adjustable sample precision (step).
public static Color getAverageColor(BufferedImage bi) {
int step = 5;
int sampled = 0;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = 0; x < bi.getWidth(); x++) {
for (int y = 0; y < bi.getHeight(); y++) {
if (x % step == 0 && y % step == 0) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
sampled++;
}
}
}
int dim = bi.getWidth()*bi.getHeight();
// Log.info("step=" + step + " sampled " + sampled + " out of " + dim + " pixels (" + String.format("%.1f", (float)(100*sampled/dim)) + " %)");
return new Color(Math.round(sumr / sampled), Math.round(sumg / sampled), Math.round(sumb / sampled));
}
How can I draw a line on the console if I have a 2D array of chars. The function I want to write is something like:
This is my first attempt, but it looks totally wrong
public static void line(char[][] mPixels, int startRow, int startColumn, int endRow, int endColumn)
{
double dY = endRow - startRow;
double dX = endColumn - startColumn;
double slope = dX / dY;
slope = Math.abs(slope);
if(slope >= 1)
{
double progress = -(dY / dX);
for(int i=startColumn; i<=endColumn; i++)
{
double j = startRow - (int) ((i-startColumn) * progress);
int yLoc = (int) (Math.round( j * 100.0 ) / 100.0);
mPixels[i][yLoc] = '*';
}
}
// print array
}
use DDA or Bresenham,...
What you have looks like DDA but you do not handle slopes correctly. You should divide by the axis with bigger amount of pixels and use it as control axis so:
if |dx|>|dy| then for goes through x = x0 -> x1 and y=y0+((x-x0)*dy/dx)
if |dx|<|dy| then for goes through y = y0 -> y1 and x=x0+((y-y0)*dx/dy)
if they are equal then use any of above.
if dx==0 and dy==0 draw just dot and no for is present
Do not forget to handle if main axis is ascending or descending (can be x++,y++ or x--,y--) also can be done on integer only without division or multiplication but that is another story
I need to draw a line from two points and what I did so far is using drawLine(x1,y1,x2,y2). But what I want to do is draw a line that intersects with these two points (x1,y1) and (x2,y2).
I don't want to just draw a line between them, here's an image of what I have and what I want to do:
you could use some mathematik. get the increase of your line. You should know the function
f(x) = mx + b. With your two points,which you allready got, you can calculate two other Points at the Border of your frame, and draw a line between them
You'll need to calculate the coordinates at which your line meets the boundaries of your graphics context.
If you have (x1,y1) and (x2,y2), calculate the x_a and y_a such that (x_a,0) and (0,y_a) lie on the line.
If x_a = 0, the line will start from the left edge. If y_a = 0, the line will start from the top edge.
Repeat for the bottom/right coords of the line.
Bresenham's line algorithm
private int sign (int x) {
return (x > 0) ? 1 : (x < 0) ? -1 : 0;
}
public void drawBresenhamLine (int xstart, int ystart, int xend, int yend, Graphics g){
int x, y, dx, dy, incx, incy, pdx, pdy, es, el, err;
dx = xend - xstart;
dy = yend - ystart;
incx = sign(dx);
incy = sign(dy);
if (dx < 0) dx = -dx;
if (dy < 0) dy = -dy;
if (dx > dy){
pdx = incx; pdy = 0;
es = dy; el = dx;
} else {
pdx = 0; pdy = incy;
es = dx; el = dy;
}
x = xstart;
y = ystart;
err = el/2;
g.drawLine (x, y, x, y);
for (int t = 0; t < el; t++)//if I multiply el a line will be longer
{
err -= es;
if (err < 0) {
err += el;
x += incx;
y += incy;
} else {
x += pdx;
y += pdy;
}
g.drawLine (x, y, x, y);
}
}
This is a homework assignment.
Work 19 5/16 is the assignment
http://sites.stuycs.org/home/courses/ml2x/dyrland-weaver/work
I am running this in the program processing, which does not require main methods.
Blob was given to us. We had to make BlobRunner on our own.
Any advice on why my code isn't doing what its supposed to would be appreciated.
FIRST FILE BlobRunner
int popSize = 4;
int wobble = 2;
int numSides = 4;
float rad = 100;
int radInt = (int) rad;
float a = sqrt(popSize);
int rootPop = (int) a;
Blob[][] blobs = new Blob[popSize/rootPop][rootPop];
/*=====================================
The trickiest part of setup is to make
the screen an appropriate size for the
grid of blobs. The grid should be just
big enough to contain all of the blobs.
====================================*/
void setup() {
size ((popSize/rootPop)*(2*(radInt+3)), rootPop*(2*(radInt+3)));
populate();
}
/*=====================================
The main purpose of draw is to go through
the array of blobs and display each.
====================================*/
void draw() {
int createdSoFar = 0;
for (int i = 0; i<rootPop; i++){
for (int j = 0; j<popSize/rootPop; j++){
if (createdSoFar < popSize){
blobs[j][i].display();
}
createdSoFar++;
}
}
}
/*=====================================
Populate the array of blobs.
You can use any values for radius, number of sides
and wobble factor that you'd like, but you must
use x and y coordinates that ensure the blobs
are drawn in a grid without overlaping each other.
Your code should work for any reasonable value
of population (i.e. something that would fit on a
normal monitor).
====================================*/
void populate() {
for (int i = 0; i < rootPop; i++){
float y = 1;
for (int j = 0; j < (popSize/rootPop); j++){
float x = 1;
blobs[j][i] = new Blob (x*(rad+3), y*(rad+3), numSides, radInt, wobble, wobble);
x=x+2;}
y=y+2;}
}
SECOND FILE Blob
/*=====================================
A Blob object is a regular polygon variant that
can have various features.
Instance Variables:
numSides: number of sides
rad: distance from the center of the polygon
to any vertext
x: x coordinate of the center
y: y coordinate of the center
xFactor: "wobble" foctor in the x direction
yFactor: "wobble" factor in the y direction
====================================*/
class Blob {
int numSides;
int rad;
float x;
float y;
int xFactor;
int yFactor;
Blob(float cx, float cy, int sides, int r, int xf, int yf ) {
x = cx;
y = cy;
numSides = sides;
rad = r;
xFactor = xf;
yFactor = yf;
}
void display() {
float nx;
float ny;
int rx, ry;
float sy;
strokeWeight(1);
beginShape();
for( float t = 0; t <= 1; t+=( 1.0/numSides ) ) {
/*
"wobble" effect is created by adding a random number to each
x and y coordinate. The larger the x and y factors, the higher
the possible wobble value could be
*/
rx = (int)random(xFactor);
ry = (int)random(yFactor);
nx = rad * cos( 2 * PI * t ) + x + rx;
ny = rad * sin( 2 * PI * t ) + y + ry;
vertex(nx, ny);
}
endShape();
}
}
Your code runs, thus it is doing what you asked it to do and nothing more.
I asked my cat to check it out though and she was all, "the guy is re-initializing his variables inside each pass of the loop, he'll never get a grid of blobs that way. Tell him to start by moving float y = 1; float x = 1; in populate() outside of the bounds of the two for loops and start debugging from there."
Then she rolled over on to her side and I patted her.