I am trying to create rain animation in android/java using canvas.
The problem is the after raindrops go out of screen, they re-appear on air instead of appearing back in cloud.
What i want is,they should appear back in cloud and the distance between each row of raindrops should remain the same.
However after they go out of screen, the distance between each row changes and they stack on each other.
How can i fix that?
counter = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 10; j++) {
if(yrain[counter]<c.getHeight()){
yrain[counter] = 400+ yAdder[counter] +j*50;
yAdder[counter]+=rainSpeed;
}else{
yAdder[counter]=0;
yrain[counter] = 400+ yAdder[counter];
}
xrain[counter] = 300+ ((50) * i);
c.drawBitmap(rain[counter], xrain[counter], yrain[counter],null);
counter++;
}
}
Here is my suggestion - I have done it some years ago when needed to show some plot animation:
Double and repeat: suppose your rain starts at point Y10 and finish at Y0. Then you can generate a random matrix with drops that has to be updated in their positions moving down in relation to Y coordinate. It goes moving up to the distance (Y10-Y0)x2.
When this circle is completed, such as:
for(step=0;step<(full_cycle_steps);step++)
{ // update Y position downwards
perform_animation();
}
then you restart the animation that repeats itself.
Smooth motion: you need to apply relative DSP (digital sinal processing), so if you have Y10 to Y0, the interval is 10 integers, take this split by 100, i.e. 10x100 will give you 1000, as you are applying it with the idea of double and repeat, then you have: 10x100x2 = 2000, that means a vector with 2000 to be moving in your canvas/graph targeted coordinates, displayed in your plotting area (1000). Of course if you don't need to have the drop moving so smoothly you can make the number of your digital x analog matrix smaller.
Here you find some reference that can be insightful:
Matrix Rain
Simulating rain
jquery-matrix-digital-rain
Taking from here, you should be able to easily complete your rain animation.
Related
I am writing a program that outputs the shortest route between two points on a map. The problem is that if the route is too long and it has many points that define its path it slows the program a lot and I am looking for a way to draw just some points instead of all the points in the array.
My approach goes as follows: the map has a zoom, each time the zoom changes check which points overlaps with the others. All the points that doesn't overlap go into the routeToDraw list and then it is drawn.
To check if the points overlap or not I have the following function:
//route is a list of latitude and longitude points
LinkedList<Point.Double> route = MapPanel.this.getGlassPane().getRoute();
LinkedList<Point.Double> routeToDraw = new LinkedList<Point.Double>();
int ovalSize = 8;
boolean compareMorePoints;
for(int i = 0; i < route.size(); i++) {
Point p1 = getScreenCoordinates(route.get(i).x, route.get(i).y);
compareMorePoints = true;
int j = i + 1;
while (j < route.size() && compareMorePoints == true) {
Point p2 = getScreenCoordinates(route.get(j).x, route.get(j).y);
if (Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)) > ovalSize ) {
routeToDraw.add(route.get(i));
compareMorePoints = false;
}
j++;
}
}
MapPanel.this.getGlassPane().setRouteToDraw(routeToDraw);
The problem is that this function is quite expensive and although it does reduce the amount of points to draw and I seem to obtain some speed after calculating routeToDraw I don't think it is worth the wait each time I zoom in or out.
The ideal solution would be something like Google Maps' does when routing, drawing a series of equidistant points that modify each time you zoom in or out and look quite nice.
Two suggestions...
(old trick)... Don't do unnecessary math inside of a loop. You can and should eliminate the sqrt function, which is an "expensive" math operation when doing distances. Just compare to the square of ovalSize. It is mathematically equivalent.
Is your list sorted in any way? If there were a convenient point in your program to sort your list (or a copy of it) before displaying, then you could very quickly:
Lop off the first and last part that is outside your zoom window in one of the coordinates (say X, if you sorted by X) by doing a binary search for the window boundary
Tighten up your loop to only look at neighbors within a window of concern, and do a sliding window instead of all-compared-to-all.
I want to create a genetic algorithm that recreates images. I have created the program for this processing but the images that evolve are not anything close to the input image.
I believe that I have a problem with my fitness function. I have tried many things from changing the polygon types that are part of the DNA, I have tried to do both a crossover and a single parent, and I tried multiple fitness functions: histogram comparison across all channels, pixel comparison, brightness comparison(for black and white images).
public void calcFitness(PImage tar){
tar.loadPixels();
image.loadPixels();
int brightness = 0;
for(int i = 0; i < image.pixels.length;i++){
brightness += Math.abs(parent.brightness(tar.pixels[i])-parent.brightness(image.pixels[i]));
}
fitness = 1.0/ (Math.pow(1+brightness,2)/2);
}
public void calculateFitness(){
int[] rHist= new int[256], gHist= new int[256], bHist = new int[256];
image.loadPixels();
//Calculate Red Histogram
for(int i =0; i<image.pixels.length;i++) {
int red = image.pixels[i] >> 16 & 0xFF;
rHist[red]++;
}
//Calculate Green Histogram
for(int i =0; i<image.pixels.length;i++) {
int green = image.pixels[i] >> 8 & 0xFF;
gHist[green]++;
}
//Calculate Blue Histogram
for(int i =0; i<image.pixels.length;i++) {
int blue = image.pixels[i] & 0xFF;
bHist[blue]++;
}
//Compare the target histogram and the current one
for(int i = 0; i < 256; i++){
double totalDiff = 0;
totalDiff += Math.pow(main.rHist[i]-rHist[i],2)/2;
totalDiff += Math.pow(main.gHist[i]-gHist[i],2)/2;
totalDiff += Math.pow(main.bHist[i]-bHist[i],2)/2;
fitness+=Math.pow(1+totalDiff,-1);
}
}
public void evaluate(){
int totalFitness = 0;
for(int i = 0; i<POPULATION_SIZE;i++){
population[i].calcFitness(target);
//population[i].calculateFitness();
totalFitness+=population[i].fitness;
}
if(totalFitness>0) {
for (int i = 0; i < POPULATION_SIZE; i++) {
population[i].prob = population[i].fitness / totalFitness;
}
}
}
public void selection() {
SmartImage[] newPopulation = new SmartImage[POPULATION_SIZE];
for (int i = 0; i < POPULATION_SIZE; i++) {
DNA child;
DNA parentA = pickOne();
DNA parentB = pickOne();
child = parentA.crossover(parentB);
child.mutate(mutationRate);
newPopulation[i] = new SmartImage(parent, child, target.width, target.height);
}
population = newPopulation;
generation++;
}
What I expect from this is to get a general shape and color that is similar to my target image but all I get is random polygons with random colors and alphas.
The code looks fine at first glance. You should first check that your code is capable of converging to a target at all , for example by feeding a target image that is either generated by your algorithm with a random genome (or a very simple image that it should be easily recreated by your algorithm).
You are using the SAD (sum of absolute differences) metric between pixels to calculate fitness. You can try using SSD (sum of squared differences) like you are doing in the histogram difference method but between pixels or blocks, that will heavily penalize large differences so the remaining images won't be too different from the target. You can try using a more perceptual image space like HSV so the images will be closer visually even if they are farther in RGB space.
I think comparing the histogram of the entire image may be too lax, as there are many different images that will result in the same histogram. Comparing individual pixels may be too strict, the image needs to be aligned very precisely to get low differences, so everything gets low fitness values unless you are very lucky so the convergence will be too slow. I would recommend that you compare the histogram between overlapping blocks, and don't use all the 256 levels, use only about 16 levels or so (or use some kind of overlapping).
Read about Histogram of oriented gradients (HOG) and other similar techniques to get ideas to improve your fitness function. I took an online course about object recognition in images, Coursera - Deteccion de Objetos by the University of Barcelona but it's in Spanish. I'm pretty sure you can find similar study materials in English.
Edit: before trying something more complex a good idea would be doing the SAD or SSD on the average of each overlapping block (which would have a similar effect to strongly blurring the reference and generated images and then comparing the pixels, but faster). The fitness function should be resilient against small changes. An image that it's shifted by a few pixels or that is very similar after discarding the low-level detail should have much better fitness than a very different image and I think blurring will have that effect.
I am now building a project based on the sample color blob tracking method. I used bounding rectangles around the contours to indicate the blobs. Now I want to improve this algorithm by using an error correction method. What I do now is simply summing up the pixels in the rect region using elemsum method and calculate the average intensity and set it as the new blob detection parameter in each frame. However, the problem is that it is not accurate since those pixels outside the contour but inside the bounding rect will be counted as well. And the result is poor.
In order to solve the problem, I used another a straightforward way to loop through each pixel in the rectangle region (which is a submat), and set all pixel values out of range to the desired (or previous) hsv scalar. Then sum up all the pixels again and calculate the average intensity. This would much more accurate and easily solves the problem. The problem is that the program runs too slow on the phone (with around 1 frame per sec), though the result is accurate.
I found some sources online on how to do it in c++ using mat.forEach. I do not want to do the ndk thing and I would like to know if there is a more efficient way to do it in Java (Android).
UPDATE:
It turned out I can solve the problem by simply reducing the sampling rate. Instead of calculating the average intensity of all pixels, just a few number of them would do the job. My code:
for (int i=0; i< bounding_rect_hsv.rows();i+=10){
for (int j=0; j<bounding_rect_hsv.cols();j+=10){
double[] data = bounding_rect_hsv.get(i, j);
for (int k = 0; k < 3; k++){
if (data[k] > new_hsvColor.val[k] + 30 || data[k] < new_hsvColor.val[k] - 30) {
data[k] = new_hsvColor.val[k];
}
}
bounding_rect_hsv.put(i, j, data); //Puts element back into matrix
}
}
My source code:
Rect rect = Imgproc.boundingRect(points);
// draw enclosing rectangle (all same color, but you could use variable i to make them unique)
Imgproc.rectangle(original_frame, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 0, 255), 3);
//Todo: use the bounding rectangular to calculate average intensity (turn the pixels out of the contour to new_hsvColor)
//Just change the boundary values would be enough
bounding_rect_rgb = original_frame.submat(rect);
Imgproc.cvtColor(bounding_rect_rgb, bounding_rect_hsv, Imgproc.COLOR_RGB2HSV_FULL);
//Todo: change the logic so that pixels outside the contour will be changed to new_hsvColor
for (int i=0; i< bounding_rect_hsv.rows();i++){
for (int j=0; j<bounding_rect_hsv.cols();j++){
double[] data = bounding_rect_hsv.get(i, j);
for (int k = 0; k < 3; k++){
if (data[k] > new_hsvColor.val[k] + 30 || data[k] < new_hsvColor.val[k] - 30)
data[k] = new_hsvColor.val[k];
}
bounding_rect_hsv.put(i, j, data); //Puts element back into matrix
}
}
If you want to compute the mean value of pixels inside a contour you can simply:
Create a mask, using drawContours with parameter CV_FILLED and color Scalar(255) on a black (Scalar(0)) initialized CV_8UC1 image with same size as the original image.
Use mean to compute the mean of pixels under the mask.
You also don't need to convert to HSV every region (Rect), but you can convert the whole image once, and then access the desired region directly on the HSV image.
In the general case you want to sum the pixel values of a lot of rectangular regions, you may prefer to compute the integral image and compute the sum as the difference of values at bottom-right and top-left rectangle positions.
So I am making a tetris game and one of the problems I am running into is piece rotation. I know I can just hard code it but thats not the right way to do it. The way the system works is I have a 2d array of an object 'Tile' the 'Tile' object has x, y coords, boolean isActive, and color. The boolean isActive basically tells the computer which tiles are actually being used (Since tetris shapes are not perfect quadrilaterals).
Here is how I would make a shape in my system:
public static Tile[][] shapeJ() {
Tile[][] tile = new Tile[3][2];
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 2; y++) {
tile[x][y] = new Tile(false, Color.blue);
}
}
tile[0][0].setActive(true);
tile[0][0].setX(0);
tile[0][0].setY(0);
tile[1][0].setActive(true);
tile[1][0].setX(50);
tile[1][0].setY(0);
tile[2][0].setActive(true);
tile[2][0].setX(100);
tile[2][0].setY(0);
tile[2][1].setActive(true);
tile[2][1].setX(100);
tile[2][1].setY(50);
return tile;
}
Now I need to rotate this object, I do not know how to do that without hard coding the positions. There has to be an algorithm for it. Can anyone offer some help?
A good way that I used when writing a tetris clone is to use rotational matrices:
http://en.wikipedia.org/wiki/Rotation_matrix
So the coordinates (x',y') of the point (x,y) after rotation are:
x' = x*cos(theta) - y*sin(theta);
y' = x*sin(theta) + y*cos(theta);
Where theta is the angle of rotation(+-90 degrees or +-PI/2 radians for the java functions that I know)
In this case the blocks are rotated around the origin (0, 0) so you either have to have the coordinates of the block in special "block space" that then gets transposed onto "field space" or you take away the offset of the block so that it is centered at the origin every iteration.
I hope that helps, I am happy to answer specific questions in the comments.
I'm trying to make balls fall from the top of the window. I store ball objects in an ArrayList and, at the moment, I am doing this.
for (int i = 0; i < balls.size(); i++) {
Ball b = (Ball) balls.get(i);
if (b.isVisible()) {
b.move();
}
the move function just changes the y co-ordinate of the ball so it drops down the screen.
At the moment, it is all being painted at exactly the same time and fall at exactly the same time.
e.g. http://puu.sh/xsGF
How do I make it so they fall at random intervals?
My move() function is as follows.
public void move() {
if (y > 480) {
this.setVisible(false);
System.out.println("GONE");
}
y += 1;
}
You could add balls randomly during the game loop.
//add new balls randomly here:
if(<randomtest>) {
balls.add(new Ball());
}
for (int i = 0; i < balls.size(); i++) {
Ball b = (Ball) balls.get(i);
if (b.isVisible()) {
b.move();
}
else {
//also might be good idea to tidy any invisible balls here
//if you do this make sure you reverse the for loop
}
}
There are 2 things you can do:
Add a Timer. When the Timer goes off (every 10 ms for example), select a random ball, and let that one drop 1px. (Mind, you will get balls that will fall at different speeds at different times, because of the random factor)
Use a random value for the speed when initializing the ball. Increase the y coordinate by that speed value, so the balls will all fall at a different rate through the sceen.
The simplest approach, if you want constant velocity, is to place them im random positions putside the top of your viewport.
Since I guess you already draw them outside the screen just add a random displacement there and you are done. eg:
ball.y = -radius + random.nextInt(100);
Ok, seeing your move function, this is not really physically correct. You should have a acceleration. This makes the ball fall more realistically (of course there is air resistance etc, but I think this is enough for now). In order the let them fall at random times, you could either add them at random times (make them existing/visible at random time instances) or so.
class Ball {
private double acc = 9.81; // or some other constant, depending on the framerate
private double velocity = 0;
private double startFallTime = Math.random()*100; // set from outside, not here!
public void move() {
// check if ball is already here
if (startFallTime-- > 0) return;
if (y > 480) {
this.setVisible(false);
System.out.println("GONE");
}
velocity += acc;
y += velocity;
}
}
EDIT: Of course the acceleration stuff is optional, depending on what you want. If you want linear movement, then your approach is fine, it just looks better if the ball has an acceleration. ;) Also, I recommend adding the balls at random instances and not work with this startFallTime that I used, because this is physically not really correct. Depends on your needs though, so you have to figure out the right way by yourself.