Reaction-Diffusion algorithm on Processing + Multithreading - multithreading

I have made an implementation of the Reaction-Diffusion algorithm on Processing 3.1.1, following a video tutorial. I have made some adaptations on my code, like implementing it on a torus space, instead of a bounded box, like the video.
However, I ran into this annoying issue, that the code runs really slow, proportional to the canvas size (larger, slower). With that, I tried optmizing the code, according to my (limited) knowledge. The main thing I did was to reduce the number of loops running.
Even then, my code still ran quite slow.
Since I have noticed that with a canvas of 50 x 50 in size, the algorithm ran at a good speed, I tried making it multithreaded, in such a way that the canvas would be divided between the threads, and each thread would run the algorithm for a small region of the canvas.
All threads read from the current state of the canvas, and all write to the future state of the canvas. The canvas is then updated using Processing's pixel array.
However, even with multithreading, I didn't see any performance improvement. By the contrary, I saw it getting worse. Now sometimes the canvas flicker between a rendered state and completely white, and in some cases, it doesn't even render.
I'm quite sure that I'm doing something wrong, or I may be taking the wrong approach to optimizing this algorithm. And now, I'm asking for help to understand what I'm doing wrong, and how I could fix or improve my code.
Edit: Implementing ahead of time calculation and rendering using a buffer of PImage objects has removed flickering, but the calculation step on the background doesn't run fast enough to fill the buffer.
My Processing Sketch is below, and thanks in advance.
ArrayList<PImage> buffer = new ArrayList<PImage>();
Thread t;
Buffer b;
PImage currentImage;
Point[][] grid; //current state
Point[][] next; //future state
//Reaction-Diffusion algorithm parameters
final float dA = 1.0;
final float dB = 0.5;
//default: f = 0.055; k = 0.062
//mitosis: f = 0.0367; k = 0.0649
float feed = 0.055;
float kill = 0.062;
float dt = 1.0;
//multi-threading parameters to divide canvas
int threadSizeX = 50;
int threadSizeY = 50;
//red shading colors
color red = color(255, 0, 0);
color white = color(255, 255, 255);
color black = color(0, 0, 0);
//if redShader is false, rendering will use a simple grayscale mode
boolean redShader = true;
//simple class to hold chemicals A and B amounts
class Point
{
float a;
float b;
Point(float a, float b)
{
this.a = a;
this.b = b;
}
}
void setup()
{
size(300, 300);
//initialize matrices with A = 1 and B = 0
grid = new Point[width][];
next = new Point[width][];
for (int x = 0; x < width; x++)
{
grid[x] = new Point[height];
next[x] = new Point[height];
for (int y = 0; y < height; y++)
{
grid[x][y] = new Point(1.0, 0.0);
next[x][y] = new Point(1.0, 0.0);
}
}
int a = (int) random(1, 20); //seed some areas with B = 1.0
for (int amount = 0; amount < a; amount++)
{
int siz = 2;
int x = (int)random(width);
int y = (int)random(height);
for (int i = x - siz/2; i < x + siz/2; i++)
{
for (int j = y - siz/2; j < y + siz/2; j++)
{
int i2 = i;
int j2 = j;
if (i < 0)
{
i2 = width + i;
} else if (i >= width)
{
i2 = i - width;
}
if (j < 0)
{
j2 = height + j;
} else if (j >= height)
{
j2 = j - height;
}
grid[i2][j2].b = 1.0;
}
}
}
initializeThreads();
}
/**
* Divide canvas between threads
*/
void initializeThreads()
{
ArrayList<Reaction> reactions = new ArrayList<Reaction>();
for (int x1 = 0; x1 < width; x1 += threadSizeX)
{
for (int y1 = 0; y1 < height; y1 += threadSizeY)
{
int x2 = x1 + threadSizeX;
int y2 = y1 + threadSizeY;
if (x2 > width - 1)
{
x2 = width - 1;
}
if (y2 > height - 1)
{
y2 = height - 1;
}
Reaction r = new Reaction(x1, y1, x2, y2);
reactions.add(r);
}
}
b = new Buffer(reactions);
t = new Thread(b);
t.start();
}
void draw()
{
if (buffer.size() == 0)
{
return;
}
PImage i = buffer.get(0);
image(i, 0, 0);
buffer.remove(i);
//println(frameRate);
println(buffer.size());
//saveFrame("output/######.png");
}
/**
* Faster than calling built in pow() function
*/
float pow5(float x)
{
return x * x * x * x * x;
}
class Buffer implements Runnable
{
ArrayList<Reaction> reactions;
boolean calculating = false;
public Buffer(ArrayList<Reaction> reactions)
{
this.reactions = reactions;
}
public void run()
{
while (true)
{
if (buffer.size() < 1000)
{
calculate();
if (isDone())
{
buffer.add(currentImage);
Point[][] temp;
temp = grid;
grid = next;
next = temp;
calculating = false;
}
}
}
}
boolean isDone()
{
for (Reaction r : reactions)
{
if (!r.isDone())
{
return false;
}
}
return true;
}
void calculate()
{
if (calculating)
{
return;
}
currentImage = new PImage(width, height);
for (Reaction r : reactions)
{
r.calculate();
}
calculating = true;
}
}
class Reaction
{
int x1;
int x2;
int y1;
int y2;
Thread t;
public Reaction(int x1, int y1, int x2, int y2)
{
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
public void calculate()
{
Calculator c = new Calculator(x1, y1, x2, y2);
t = new Thread(c);
t.start();
}
public boolean isDone()
{
if (t.getState() == Thread.State.TERMINATED)
{
return true;
} else
{
return false;
}
}
}
class Calculator implements Runnable
{
int x1;
int x2;
int y1;
int y2;
//weights for calculating the Laplacian for A and B
final float[][] laplacianWeights = {{0.05, 0.2, 0.05},
{0.2, -1, 0.2},
{0.05, 0.2, 0.05}};
/**
* x1, x2, y1, y2 delimit a rectangle. The object will only work within it
*/
public Calculator(int x1, int y1, int x2, int y2)
{
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
//println("x1: " + x1 + ", y1: " + y1 + ", x2: " + x2 + ", y2: " + y2);
}
#Override
public void run()
{
reaction();
show();
}
public void reaction()
{
for (int x = x1; x <= x2; x++)
{
for (int y = y1; y <= y2; y++)
{
float a = grid[x][y].a;
float b = grid[x][y].b;
float[] l = laplaceAB(x, y);
float a2 = reactionDiffusionA(a, b, l[0]);
float b2 = reactionDiffusionB(a, b, l[1]);
next[x][y].a = a2;
next[x][y].b = b2;
}
}
}
float reactionDiffusionA(float a, float b, float lA)
{
return a + ((dA * lA) - (a * b * b) + (feed * (1 - a))) * dt;
}
float reactionDiffusionB(float a, float b, float lB)
{
return b + ((dB * lB) + (a * b * b) - ((kill + feed) * b)) * dt;
}
/**
* Calculates Laplacian for both A and B at same time, to reduce amount of loops executed
*/
float[] laplaceAB(int x, int y)
{
float[] l = {0.0, 0.0};
for (int i = x - 1; i < x + 2; i++)
{
for (int j = y - 1; j < y + 2; j++)
{
int i2 = i;
int j2 = j;
if (i < 0)
{
i2 = width + i;
} else if (i >= width)
{
i2 = i - width;
}
if (j < 0)
{
j2 = height + j;
} else if (j >= height)
{
j2 = j - height;
}
int weightX = (i - x) + 1;
int weightY = (j - y) + 1;
l[0] += laplacianWeights[weightX][weightY] * grid[i2][j2].a;
l[1] += laplacianWeights[weightX][weightY] * grid[i2][j2].b;
}
}
return l;
}
public void show()
{
currentImage.loadPixels();
//renders the canvas using the pixel array
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
float a = next[x][y].a;
float b = next[x][y].b;
int pix = x + y * width;
float diff = (a - b);
color c;
if (redShader) //aply red shading
{
float thresh = 0.5;
if (diff < thresh)
{
float diff2 = map(pow5(diff), 0, pow5(thresh), 0, 1);
c = lerpColor(black, red, diff2);
} else
{
float diff2 = map(1 - pow5(-diff + 1), 1 - pow5(-thresh + 1), 1, 0, 1);
c = lerpColor(red, white, diff2);
}
} else //apply gray scale shading
{
c = color(diff * 255, diff * 255, diff * 255);
}
currentImage.pixels[pix] = c;
}
}
currentImage.updatePixels();
}
}

A programmer had a problem. He thought “I know, I’ll solve it with threads!”. has Now problems. two he
Processing uses a single rendering thread.
It does this for good reason, and most other renderers do the same thing. In fact, I don't know of any multi-threaded renderers.
You should only change what's on the screen from Processing's main rendering thread. In other words, you should only change stuff from Processing's functions, not your own thread. This is what's causing the flickering you're seeing. You're changing stuff as it's being drawn to the screen, which is a horrible idea. (And it's why Processing uses a single rendering thread in the first place.)
You could try to use your multiple threads to do the processing, not the rendering. But I highly doubt that's going to be worth it, and like you saw, it might even make things worse.
If you want to speed up your sketch, you might also consider doing the processing ahead of time instead of in real time. Do all your calculations at the beginning of the sketch, and then just reference the results of the calculations when it's time to draw the frame. Or you could draw to a PImage ahead of time, and then just draw those.

Related

checking edge for circular movement

I want to make my dot program turn around when they reach edge
so basically i just simply calculate
x = width/2+cos(a)*20;
y = height/2+sin(a)*20;
it's make circular movement. so i want to make this turn around by checking the edge. i also already make sure that y reach the if condition using println command
class particles {
float x, y, a, r, cosx, siny;
particles() {
x = width/2; y = height/2; a = 0; r = 20;
}
void display() {
ellipse(x, y, 20, 20);
}
void explode() {
a = a + 0.1;
cosx = cos(a)*r;
siny = sin(a)*r;
x = x + cosx;
y = y + siny;
}
void edge() {
if (x>width||x<0) cosx*=-1;
if (y>height||y<0) siny*=-1;
}
}
//setup() and draw() function
particles part;
void setup(){
size (600,400);
part = new particles();
}
void draw(){
background(40);
part.display();
part.explode();
part.edge();
}
they just ignore the if condition
There is no problem with your check, the problem is with the fact that presumably the very next time through draw() you ignore what you did in response to the check by resetting the values of cosx and siny.
I recommend creating two new variables, dx and dy ("d" for "direction") which will always be either +1 and -1 and change these variables in response to your edge check. Here is a minimal example:
float a,x,y,cosx,siny;
float dx,dy;
void setup(){
size(400,400);
background(0);
stroke(255);
noFill();
x = width/2;
y = height/2;
dx = 1;
dy = 1;
a = 0;
}
void draw(){
ellipse(x,y,10,10);
cosx = dx*20*cos(a);
siny = dy*20*sin(a);
a += 0.1;
x += cosx;
y += siny;
if (x > width || x < 0)
dx = -1*dx;
if (y > height || y < 0)
dy = -1*dy;
}
When you run this code you will observe the circles bouncing off the edges:

Converting an svg arc to lines

I am trying to convert an SVG arc to a series of line segments. The background is, that I want to draw an arc using (reportlab)[http://www.reportlab.com/].
The svg gives me these parameters (accoring to here).
rx,ry,x-axis-rotation,large-arc-flag,sweep-flag,dx,dy
Now I need to determine lines following this arcs. But I do not understand how I can convert this to something geometrical more usable.
How would I determine the center of the ellipse arc and its rotation?
SVG elliptic arcs are really tricky and took me a while to implement it (even following the SVG specs). I ended up with something like this in C++:
//---------------------------------------------------------------------------
class svg_usek // virtual class for svg_line types
{
public:
int pat; // svg::pat[] index
virtual void reset(){};
virtual double getl (double mx,double my){ return 1.0; };
virtual double getdt(double dl,double mx,double my){ return 0.1; };
virtual void getpnt(double &x,double &y,double t){};
virtual void compute(){};
virtual void getcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val){};
virtual void setcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val,int &an,int &ad,int &av){};
};
//---------------------------------------------------------------------------
class svg_ela:public svg_usek // sweep = 0 arc goes from line p0->p1 CW
{ // sweep = 1 arc goes from line p0->p1 CCW
public: // larc is unused if |da|=PI
double x0,y0,x1,y1,a,b,alfa; int sweep,larc;
double sx,sy,a0,a1,da,ang; // sx,sy rotated center by ang
double cx,cy; // real center
void reset() { x0=0; y0=0; x1=0; y1=0; a=0; b=0; alfa=0; sweep=false; larc=false; compute(); }
double getl (double mx,double my);
// double getdt(double dl,double mx,double my);
double getdt(double dl,double mx,double my) { int n; double dt; dt=divide(dl,getl(mx,my)); n=floor(divide(1.0,dt)); if (n<1) n=1; return divide(1.0,n); }
void getpnt(double &x,double &y,double t);
void compute();
void getcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val);
void setcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val,int &an,int &ad,int &av);
svg_ela() {}
svg_ela(svg_ela& a) { *this=a; }
~svg_ela() {}
svg_ela* operator = (const svg_ela *a) { *this=*a; return this; }
//svg_ela* operator = (const svg_ela &a) { ...copy... return this; }
};
//---------------------------------------------------------------------------
void svg_ela::getpnt(double &x,double &y,double t)
{
double c,s,xx,yy;
t=a0+(da*t);
xx=sx+a*cos(t);
yy=sy+b*sin(t);
c=cos(-ang);
s=sin(-ang);
x=xx*c-yy*s;
y=xx*s+yy*c;
}
//---------------------------------------------------------------------------
void svg_ela::compute()
{
double ax,ay,bx,by; // body
double vx,vy,l,db;
int _sweep;
double c,s,e;
ang=pi-alfa;
_sweep=sweep;
if (larc) _sweep=!_sweep;
e=divide(a,b);
c=cos(ang);
s=sin(ang);
ax=x0*c-y0*s;
ay=x0*s+y0*c;
bx=x1*c-y1*s;
by=x1*s+y1*c;
ay*=e; // transform to circle
by*=e;
sx=0.5*(ax+bx); // mid point between A,B
sy=0.5*(ay+by);
vx=(ay-by);
vy=(bx-ax);
l=divide(a*a,(vx*vx)+(vy*vy))-0.25;
if (l<0) l=0;
l=sqrt(l);
vx*=l;
vy*=l;
if (_sweep)
{
sx+=vx;
sy+=vy;
}
else{
sx-=vx;
sy-=vy;
}
a0=atanxy(ax-sx,ay-sy);
a1=atanxy(bx-sx,by-sy);
// ay=divide(ay,e);
// by=divide(by,e);
sy=divide(sy,e);
da=a1-a0;
if (fabs(fabs(da)-pi)<=_acc_zero_ang) // half arc is without larc and sweep is not working instead change a0,a1
{
db=(0.5*(a0+a1))-atanxy(bx-ax,by-ay);
while (db<-pi) db+=pi2; // db<0 CCW ... sweep=1
while (db>+pi) db-=pi2; // db>0 CW ... sweep=0
_sweep=0;
if ((db<0.0)&&(!sweep)) _sweep=1;
if ((db>0.0)&&( sweep)) _sweep=1;
if (_sweep)
{
// a=0; b=0;
if (da>=0.0) a1-=pi2;
if (da< 0.0) a0-=pi2;
}
}
else if (larc) // big arc
{
if ((da< pi)&&(da>=0.0)) a1-=pi2;
if ((da>-pi)&&(da< 0.0)) a0-=pi2;
}
else{ // small arc
if (da>+pi) a1-=pi2;
if (da<-pi) a0-=pi2;
}
da=a1-a0;
// realny stred
c=cos(+ang);
s=sin(+ang);
cx=sx*c-sy*s;
cy=sx*s+sy*c;
}
//---------------------------------------------------------------------------
The atanxy(x,y) is the same as atan2(y,x). You can ignore class svg_usek. Usage of svg_ela is simple first feed the SVG parameters to it:
x0,y0 is start point (from previous <path> element)
x1,y1 is endpoint (x0+dx,y0+dy)
a,b are as yours rx,ry
alfa rotation angle [rad] so you need to convert from degrees...
sweep,larc are as yours.
And then call svg_ela::compute(); that will compute all variables needed for interpolation. When this initialization is done then to obtain any point from the arc just call svg_ela::getpnt(x,y,t); where x,y is the returned coordinate and t=<0,1> is input parameter. All the other methods are not important for you. To render your ARC just do this:
svg_ela arc; // your initialized arc here
int e; double x,y,t;
arc.getpnt(x,y,0.0);
Canvas->MoveTo(x,y);
for (e=1,t=0.0;e;t+=0.02)
{
if (t>=1.0) { t=1.0; e=0; }
arc.getpnt(x,y,t);
Canvas->LineTo(x,y);
}
Do not forget that SVG <g> and <path> can have transform matrices so you should apply them after each svg_ela::getpnt(x,y,t) call.
If you are interested how the stuff works compute() simply:
rotates the space so the ellipse semi-axises are axis aligned.
scale the space so ellipse becomes circle.
compute center point for circle
center lies on line that is perpendicular to line (x0,y0),(x1,y1) and also lies on its midpoint. The distance is computed by Pytagoras and direction from sweep and larc combination.
scale back to ellipse
rotate back
Now we have real center position so also compute the real endpoint angles relative to it. Now for each point on ellipse it is enough to compute it by standard parametric equation of ellipse and rotate to desired position which is what getpnt(x,y,t) does.
Hope it helps a bit.
Here related QA:
Express SVG arc as series of curves
with some images explaining the math behind SVG arcs (using the same variable names as here)
For my Java SVG application I needed a conversion of path arc to lines. I used the above code and converted it into a Java class and performed some cleanup.
package de.berndbock.tinysvg.helper;
/**
* Breaks down SVG arcs into line segments.
*
* #author Bernd Bock <chef#bernd-bock.de>
*/
public class ArcSegmenter {
private static final double PI2 = Math.PI * 2;
private static final double ACC_ZERO_ANG = 0.000001 * Math.PI / 180.0;
private final double x0;
private final double y0;
private final double x1;
private final double y1;
private final double a;
private final double b;
private final double alfa;
private final boolean sweep;
private final boolean larc;
private double sx, sy, a0, a1, da, ang; // sx, sy rotated center by ang
// private double cx, cy; // real center
public ArcSegmenter(double x0, double y0, double x1, double y1 , double a, double b, double alfa, int sweep, int larc) {
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
this.a = a;
this.b = b;
this.alfa = alfa;
this.sweep = sweep != 0;
this.larc = larc != 0;
compute();
}
private void compute() {
double ax, ay, bx, by; // body
double vx, vy, l, db;
boolean _sweep;
double c, s, e;
ang = Math.PI - alfa;
_sweep = sweep;
if (larc) {
_sweep = !_sweep;
}
e = a / b;
c = Math.cos(ang);
s = Math.sin(ang);
ax = x0 * c - y0 * s;
ay = x0 * s + y0 * c;
bx = x1 * c - y1 * s;
by = x1 * s + y1 * c;
ay *= e; // transform to circle
by *= e;
sx = 0.5 * (ax + bx); // mid point between A,B
sy = 0.5 * (ay + by);
vx = (ay - by);
vy = (bx - ax);
l = a * a / (vx * vx + vy * vy) - 0.25;
if (l < 0) {
l = 0;
}
l = Math.sqrt(l);
vx *= l;
vy *= l;
if (_sweep) {
sx += vx;
sy += vy;
}
else {
sx -= vx;
sy -= vy;
}
a0 = Math.atan2(ay - sy, ax - sx);
a1 = Math.atan2(by - sy, bx - sx);
sy = sy / e;
da = a1 - a0;
if (Math.abs(Math.abs(da) - Math.PI) <= ACC_ZERO_ANG) { // half arc is without larc and sweep is not working instead change a0,a1
db = (0.5 * (a0 + a1)) - Math.atan2(by - ay, bx - ax);
while (db < -Math.PI) {
db += PI2; // db<0 CCW ... sweep=1
}
while (db > Math.PI) {
db -= PI2; // db>0 CW ... sweep=0
}
_sweep = false;
if ((db < 0.0) && (!sweep)) {
_sweep = true;
}
if ((db > 0.0) && ( sweep)) {
_sweep = true;
}
if (_sweep) {
if (da >= 0.0) {
a1 -= PI2;
}
if (da < 0.0) {
a0 -= PI2;
}
}
}
else if (larc) { // big arc
if ((da < Math.PI) && (da >= 0.0)) {
a1 -= PI2;
}
if ((da > -Math.PI) && (da < 0.0)) {
a0 -= PI2;
}
}
else { // small arc
if (da > Math.PI) {
a1 -= PI2;
}
if (da < -Math.PI) {
a0 -= PI2;
}
}
da = a1 - a0;
// center point calculation:
// c = Math.cos(ang);
// s = Math.sin(ang);
// cx = sx * c - sy * s;
// cy = sx * s + sy * c;
}
public Point getpnt(double t) {
Point result = new Point();
double c, s, x, y;
t = a0 + da * t;
x = sx + a * Math.cos(t);
y = sy + b * Math.sin(t);
c = Math.cos(-ang);
s = Math.sin(-ang);
result.x = x * c - y * s;
result.y = x * s + y * c;
return result;
}
// public Point getCenterPoint() {
// return new Point(cx, cy);
// }
}
If you need the center point, then uncomment the respective lines.
Sample code to give you an idea of the usage:
ArcSegmenter segmenter = new ArcSegmenter(currentPoint.x, currentPoint.y, endPoint.x, endPoint.y, rx, ry, phi, sf, lf);
Point p1, p2;
p1 = segmenter.getpnt(0.0);
Line line;
for (double t = increment; t < 1.000001f; t += increment) {
p2 = segmenter.getpnt(t);
line = new Line(null, parent, p1.x, p1.y, p2.x, p2.y);
elements.add(line);
p1 = p2;
}

I want to track 2 colours, but only record the movement of those two colours and hide the video feed

For context: I am going to analyze the breathing movement of parents during kangaroo mother care and I wish to respect their privacy by not recording them, but only the movement of stickers I placed on their chest and stomach.
So far, I'm able to track 2 colours based on webcam input through the code below. However, I would like to record only the tracked colours instead of the webcam feed as to preserve the privacy of the parent.
Does anybody know how to add a background colour, whilst still being able to track colour?
import processing.video.*;
Capture video;
final int TOLERANCE = 20;
float XRc = 0;// XY coordinate of the center of the first target
float YRc = 0;
float XRh = 0;// XY coordinate of the center of the second target
float YRh = 0;
int ii=0; //Mouse click counter
color trackColor; //The first color is the center of the robot
color trackColor2; //The second color is the head of the robot
void setup() {
size(640,480);
video = new Capture(this,640,480);
video.start();
trackColor = color(255,0,0);
trackColor2 = color(255,0,0);
smooth();
}
void draw() {
background(0);
if (video.available()) {
video.read();
}
video.loadPixels();
image(video,0,0);
float r2 = red(trackColor);
float g2 = green(trackColor);
float b2 = blue(trackColor);
float r3 = red(trackColor2);
float g3 = green(trackColor2);
float b3 = blue(trackColor2);
int somme_x = 0, somme_y = 0;
int compteur = 0;
int somme_x2 = 0, somme_y2 = 0;
int compteur2 = 0;
for(int x = 0; x < video.width; x++) {
for(int y = 0; y < video.height; y++) {
int currentLoc = x + y*video.width;
color currentColor = video.pixels[currentLoc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
if(dist(r1,g1,b1,r2,g2,b2) < TOLERANCE) {
somme_x += x;
somme_y += y;
compteur++;
}
else if(compteur > 0) {
XRc = somme_x / compteur;
YRc = somme_y / compteur;
}
if(dist(r1,g1,b1,r3,g3,b3) < TOLERANCE) {
somme_x2 += x;
somme_y2 += y;
compteur2++;
}
else if(compteur2 > 0) {
XRh = somme_x2 / compteur2;
YRh = somme_y2 / compteur2;
}
}
}
if(XRc != 0 || YRc != 0) { // Draw a circle at the first target
fill(trackColor);
strokeWeight(0.05);
stroke(0);
ellipse(XRc,YRc,20,20);
}
if(XRh != 0 || YRh != 0) {// Draw a circle at the second target
fill(trackColor2);
strokeWeight(0.05);
stroke(0);
ellipse(XRh,YRh,20,20);
}
}
void mousePressed() {
if (mousePressed && (mouseButton == RIGHT)) { // Save color where the mouse is clicked in trackColor variable
if(ii==0){
if (mouseY>480){mouseY=0;mouseX=0;}
int loc = mouseX + mouseY*video.width;
trackColor = video.pixels[loc];
ii=1;
}
else if(ii==1){
if (mouseY>480){mouseY=0;mouseX=0;}
int loc2 = mouseX + mouseY*video.width;
trackColor2 = video.pixels[loc2];
ii=2;
}
}
}
Try adding the background(0); right before you draw the first circle. It should cover the video and you can draw the circles on top of it.
Regards
Jose

Lanczos Resampling error

I have written an image resizer using Lanczos re-sampling. I've taken the implementation straight from the directions on wikipedia. The results look good visually, but for some reason it does not match the result from Matlab's resize with Lanczos very well (in pixel error).
Does anybody see any errors? This is not my area of expertise at all...
Here is my filter (I'm using Lanczos3 by default):
double lanczos_size_ = 3.0;
inline double sinc(double x) {
double pi = 3.1415926;
x = (x * pi);
if (x < 0.01 && x > -0.01)
return 1.0 + x*x*(-1.0/6.0 + x*x*1.0/120.0);
return sin(x)/x;
}
inline double LanczosFilter(double x) {
if (std::abs(x) < lanczos_size_) {
double pi = 3.1415926;
return sinc(x)*sinc(x/lanczos_size_);
} else {
return 0.0;
}
}
And my code to resize the image:
Image Resize(Image& image, int new_rows, int new_cols) {
int old_cols = image.size().cols;
int old_rows = image.size().rows;
double col_ratio =
static_cast<double>(old_cols)/static_cast<double>(new_cols);
double row_ratio =
static_cast<double>(old_rows)/static_cast<double>(new_rows);
// Apply filter first in width, then in height.
Image horiz_image(new_cols, old_rows);
for (int r = 0; r < old_rows; r++) {
for (int c = 0; c < new_cols; c++) {
// x is the new col in terms of the old col coordinates.
double x = static_cast<double>(c)*col_ratio;
// The old col corresponding to the closest new col.
int floor_x = static_cast<int>(x);
horiz_image[r][c] = 0.0;
double weight = 0.0;
// Add up terms across the filter.
for (int i = floor_x - lanczos_size_ + 1; i < floor_x + lanczos_size_; i++) {
if (i >= 0 && i < old_cols) {
double lanc_term = LanczosFilter(x - i);
horiz_image[r][c] += image[r][i]*lanc_term;
weight += lanc_term;
}
}
// Normalize the filter.
horiz_image[r][c] /= weight;
// Strap the pixel values to valid values.
horiz_image[r][c] = (horiz_image[r][c] > 1.0) ? 1.0 : horiz_image[r][c];
horiz_image[r][c] = (horiz_image[r][c] < 0.0) ? 0.0 : horiz_image[r][c];
}
}
// Now apply a vertical filter to the horiz image.
Image new_image(new_cols, new_rows);
for (int r = 0; r < new_rows; r++) {
double x = static_cast<double>(r)*row_ratio;
int floor_x = static_cast<int>(x);
for (int c = 0; c < new_cols; c++) {
new_image[r][c] = 0.0;
double weight = 0.0;
for (int i = floor_x - lanczos_size_ + 1; i < floor_x + lanczos_size_; i++) {
if (i >= 0 && i < old_rows) {
double lanc_term = LanczosFilter(x - i);
new_image[r][c] += horiz_image[i][c]*lanc_term;
weight += lanc_term;
}
}
new_image[r][c] /= weight;
new_image[r][c] = (new_image[r][c] > 1.0) ? 1.0 : new_image[r][c];
new_image[r][c] = (new_image[r][c] < 0.0) ? 0.0 : new_image[r][c];
}
}
return new_image;
}
Here is Lanczosh in one single loop. no errors.
Uses mentioned at top procedures.
void ResizeDD(
double* const pixelsSrc,
const int old_cols,
const int old_rows,
double* const pixelsTarget,
int const new_rows, int const new_cols)
{
double col_ratio =
static_cast<double>(old_cols) / static_cast<double>(new_cols);
double row_ratio =
static_cast<double>(old_rows) / static_cast<double>(new_rows);
// Now apply a filter to the image.
for (int r = 0; r < new_rows; ++r)
{
const double row_within = static_cast<double>(r)* row_ratio;
int floor_row = static_cast<int>(row_within);
for (int c = 0; c < new_cols; ++c)
{
// x is the new col in terms of the old col coordinates.
double col_within = static_cast<double>(c)* col_ratio;
// The old col corresponding to the closest new col.
int floor_col = static_cast<int>(col_within);
double& v_toSet = pixelsTarget[r * new_cols + c];
v_toSet = 0.0;
double weight = 0.0;
for (int i = floor_row - lanczos_size_ + 1; i <= floor_row + lanczos_size_; ++i)
{
for (int j = floor_col - lanczos_size_ + 1; j <= floor_col + lanczos_size_; ++j)
{
if (i >= 0 && i < old_rows && j >= 0 && j < old_cols)
{
const double lanc_term = LanczosFilter(row_within - i + col_within - j);
v_toSet += pixelsSrc[i * old_rows + j] * lanc_term;
weight += lanc_term;
}
}
}
v_toSet /= weight;
v_toSet = (v_toSet > 1.0) ? 1.0 : v_toSet;
v_toSet = (v_toSet < 0.0) ? 0.0 : v_toSet;
}
}
}
The line
for (int i = floor_x - lanczos_size_ + 1; i < floor_x + lanczos_size_; i++)
should be
for (int i = floor_x - lanczos_size_ + 1; i <= floor_x + lanczos_size_; i++)
Do not know but perhaps other mistakes linger too.
I think there is a mistake in your sinc function. Below the fraction bar you have to square pi and x. Additional you have to multiply the function with lanczos size
L(x) = **a***sin(pi*x)*sin(pi*x/a) * (pi**²**x**²**)^-1
Edit: My mistake, there is all right.

j2me drawChar abnormal character spacing?

I write some code to draw a text on a j2me canvas without using drawString.
For some reasons I can't use drawString method.
So, when I run my program, I deal with abnormal character spacing.
Please help me to solve the problem. This is my code:
public void paint(Graphics g) {
...
String str = ... ;
int x0 = 10;
int y0 = getHeight() - 50;
Font f = g.getFont();
int charWidth = 0;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
charWidth = f.charWidth(ch);
x0 += charWidth;
g.drawChar(ch, x0, y0, 0);
}
...
}
instead use this:
public void paint(Graphics g) {
...
String str = ... ;
int x0 = 10;
int y0 = getHeight() - 50;
Font f = g.getFont();
int lastWidth = 0;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
g.drawChar(ch, x0 + lastWidth, y0, 0);
lastWidth += f.charWidth(ch);
}
...
}
In your drawChar method,you use 0(it is equal to Graphics.TOP|Graphics.LEFT) so you would increase "lastWidth" after draw current char,or use another anchor(for example Graphics.TOP|Graphics.RIGHT) for drawChar.

Resources