I've searched around quite a bit for ways to do this and I finally decided to just write text by loading it from a bitmap image. This is my first go at loading separate regions of an image and using a bitmap, so I know I have some mistakes in my algorithm. (Some major ones I would bet) but I did it the best way I could think of how. IF someone would tell me what I'm doing wrong and point me in the right direction to figuring this out that would be great. From what I can tell when I send the information into the render it is correct, I get the right character and the right x,y position to translate it too, and the texture is loading just fine as well. I just can't seem to figure out why I'm not getting the right part of the image for the letter, nothing is showing up.
Here is my code.
public class StringText {
private ArrayList<TextChar> fontChar;
private final int CHAR_SIZE = 8;
private final int MARGINE = 1;
private Texture fontTexture;
private String lower;
private String upper;
private String symb;
private final int IMAGE_SIZE = 256;
public StringText(){
lower = "abcdefghijklmnopqrstuvwxyz";
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
symb = ".,?:*!+-() ";
fontChar = new ArrayList<TextChar>();
readInFont("font");
populateCheck();
}
public void drawText(String ts, int x, int y, float red, float green, float blue){
for(int a = 0; a < ts.length(); a++){
for(int b = 0; b < fontChar.size(); b++)
if(fontChar.get(b).getChar == ts.charAt(a))
render(fontChar.get(b),a, x, y, red, green, blue);
}
}
private void populateCheck(){
int charX = 0;
int charY = 0;
for(int x = 0; x < lower.length(); x++){
TextChar t = new TextChar(lower.charAt(x), charX, -8);
fontChar.add(t);
charX += CHAR_SIZE;
}
charX = 0;
for(int x = 0; x < upper.length(); x++){
TextChar t = new TextChar (upper.charAt(x), charX, -16);
fontChar.add(t);
charX += CHAR_SIZE;
}
charX = 0;
for(int x = 0; x < symb.length(); x++){
TextChar t = new TextChar(symb.charAt(x), charX, -24);
fontChar.add(t);
charX += CHAR_SIZE;
}
}
private void render(TextChar textChar, int current, int x, int y, float red, float green, float blue){
int inc = current * (CHAR_SIZE + MARGINE);
glLoadIdentity();
glTranslatef(x + inc,y,0f);
glColor3f(red, green, blue);
glScalef(1.0f, -1.0f, 1.0f);
fontTexture.bind();
System.out.println("Letter Position: " + (x+inc) + ", " + y + " Character: " + textChar.getChar);
float x0 = textChar.x/IMAGE_SIZE;
float y0 = textChar.y/IMAGE_SIZE;
float x1 = (textChar.x + CHAR_SIZE)/IMAGE_SIZE;
float y1 = (textChar.y - CHAR_SIZE)/IMAGE_SIZE;
glBegin(GL_QUADS);
glTexCoord2f( x0, -y0); glVertex2f(0,0);
glTexCoord2f(x1,-y0); glVertex2f(IMAGE_SIZE,0);
glTexCoord2f(x1,-y1); glVertex2f(IMAGE_SIZE, IMAGE_SIZE);
glTexCoord2f(x0,-y1); glVertex2f(0,IMAGE_SIZE);
glEnd();
}
private void readInFont(String s){
try {
fontTexture = TextureLoader.getTexture("PNG", new FileInputStream(new File("res/font/"+ s +".png")));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private class TextChar{
int x,y;
char getChar;
public TextChar(char s, int xa, int ya){
x = xa;
y = ya;
getChar = s;
}
}
}
I figured out a better way to do it than this by using an ASCII bitmap.
Related
I am working on a project that finds the average color of each scene from a movie and if those colors are within a certain threshold value of red then the frame will be saved as a png. The atnm1.mov reads that RuntimeException: Could not load movie file antm1.mov, and the files do not save?
import processing.video.*;
color trackColor;
float threshold;
Movie video;
void setup() {
size(1200,900);
video = new Movie(this, "antm1.mov");
video.loop();
trackColor = color(255, 0, 0);
}
void movieEvent(Movie video) {
video.read();
}
void draw() {
video.loadPixels();
image(video, 0, 0);
threshold = 80;
float avgX = 0;
float avgY = 0;
int count = 0;
// Begin loop to walk through every pixel
for (int x = 0; x < video.width; x++ ) {
for (int y = 0; y < video.height; y++ ) {
int loc = x + y * video.width;
// What is current color
color currentColor = video.pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float r2 = red(trackColor);
float g2 = green(trackColor);
float b2 = blue(trackColor);
float d = dist(r1, g1, b1, r2, g2, b2);
if (d < threshold*threshold) {
avgX += x;
avgY += y;
count++;
}
}
}
if (count >5) {
avgX = avgX / count;
avgY = avgY / count;
saveFrame("output/mov1####.png");
}
}
I wrote a code for processing and had formerly sorted pixels with selection sort. I have to hand it in and the teacher said it is taking to long like this, so I decided to divide the pixels brightness into parts of 50 and just sort it very roughly. The image that comes out isn't completely sorted though and I really don't know where it went wrong.
I doesn't have to be sorted perfectly - it's really just about having a cool-looking image as a result.
I hope some can help me and it is understandable what I mean!
Thanks in advance
PImage img;
PImage two;
PImage sorted;
int j = 0;
int x = j;
int y = x;
int u = y;
int h = u;
int d = 1;
void setup() {
size(736,1051);
img = loadImage("guy.png");
two = loadImage("guy2.png");
background(two);
}
void draw() {
loadPixels();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int loc = x + y*width;
float r = red(img.pixels[loc]);
float g = green(img.pixels[loc]);
float b = blue(img.pixels[loc]);
float av = ((r+g+b)/3.0);
pixels[loc] = color(g,b,r, 17); //I know r, g, b are switched here
}
}
updatePixels();
save("guy_coloured.png");
}
void keyPressed(){
sorted = loadImage("guy_coloured.png");
sorted.loadPixels();
image(sorted, 0, 0);
System.out.print("doing it");
for (int i = 0; i < sorted.pixels.length; i++){
color colours = sorted.pixels[i];
float b = brightness(colours);
if(b<50){
sorted.pixels[j] = sorted.pixels[i];
j++;}
}
for (int f = 0; f < img.pixels.length; f++){
color colours = sorted.pixels[f];
float b = brightness(colours);
if(b<100 && b>50){
sorted.pixels[x] = sorted.pixels[f];
x++;}
}
for (int k = 0; k < img.pixels.length; k++){
color colours = sorted.pixels[k];
float b = brightness(colours);
if(b<150 && b>100){
sorted.pixels[y] = sorted.pixels[k];
y++;}
}
for (int t = 0; t < img.pixels.length; t++){
color colours = sorted.pixels[t];
float b = brightness(colours);
if(b<200 && b>150){
sorted.pixels[u] = sorted.pixels[t];
u++;}
}
for (int o = 0; o < img.pixels.length; o++){
color colours = sorted.pixels[o];
float b = brightness(colours);
if(b>200){
sorted.pixels[h] = sorted.pixels[o];
h++;}
}
System.out.print("done");
sorted.updatePixels();
image(sorted, 0, 0);
save("guy_sorted.png");
noLoop();
}
I want the whole image to be sorted, but it gives me back the normal image with about 1/4 sorted from the top.
This is the current result:
https://imgur.com/kHffIpm
Full code including irrelevant parts: https://docs.google.com/document/d/1YC97YMq9fKcbCAn3_RvLIm1bNo72FrNnHT3obc9pp7U/edit?usp=sharing
You do not sort the pixels. What you actually do is to arrange the dark pixel at the begin of the image and overwrite the pixels which are there. If you want to sort the pixels, then you've to swap them.
Write a function which can swap 2 pixel:
void Swap(PImage toSort, int i1, int i2) {
color c = toSort.pixels[i1];
toSort.pixels[i1] = toSort.pixels[i2];
toSort.pixels[i2] = c;
}
Once some pixels have been sorted, and are arranged at the begin of the image, this area doesn't need to be investigated further.
Write a function which sorts pixels dependent on a brightness range [b_min, b_max] and start at a certain index start:
int Sort(PImage toSort, int start, float b_min, float b_max) {
for (int i = start; i < toSort.pixels.length; i++) {
float b = brightness(toSort.pixels[i]);
if (b >= b_min && b < b_max) {
Swap(toSort, i, start);
start ++;
}
}
return start;
}
Sort the image by ascending brightness. e.g:
PImage img, two, sorted;
void setup() {
size(736,1051);
img = loadImage("guy.png");
two = loadImage("guy2.png");
background(two);
}
void draw() {
loadPixels();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int loc = x + y*width;
float r = red(img.pixels[loc]), g = green(img.pixels[loc]), b = blue(img.pixels[loc]);
pixels[loc] = color(g,b,r, 17); //I know r, g, b are switched here
}
}
updatePixels();
save("guy_coloured.png");
}
void Swap(PImage toSort, int i1, int i2) {
color c = toSort.pixels[i1];
toSort.pixels[i1] = toSort.pixels[i2];
toSort.pixels[i2] = c;
}
int Sort(PImage toSort, int start, float b_min, float b_max) {
for (int i = start; i < toSort.pixels.length; i++) {
float b = brightness(toSort.pixels[i]);
if (b >= b_min && b < b_max) {
Swap(toSort, i, start);
start ++;
}
}
return start;
}
void keyPressed(){
sorted = loadImage("guy_coloured.png");
sorted.loadPixels();
image(sorted, 0, 0);
System.out.print("doing it");
int j = 0;
j = Sort(sorted, j, 0.0, 50.0);
j = Sort(sorted, j, 0.50, 100.0);
j = Sort(sorted, j, 0.100, 150.0);
j = Sort(sorted, j, 0.150, 200.0);
j = Sort(sorted, j, 0.200, 256.0);
System.out.print("done");
sorted.updatePixels();
image(sorted, 0, 0);
save("guy_sorted.png");
noLoop();
}
The study project is about warping text along bezier curves(recursive polynomial form) on processing using the geomerative library to get shape from a .ttf type font file.(It needs a ttf file in the data directory to function.) currently, the sketch seems to throws errors when
fill(any color); is used at the part where the code draws the character shapes and the bezier curve's length gets shorter than a certain length. If fill(); is not used, the sketch seems to function okay without any errors.
The goal is to use the fill(); function to fill the characters without errors.
I've tried;
1) getting rid of the beginContour(); and endContour(); because I thought it hasn't been written properly.(I thought it was wrong because the contours should only be drawn when the shape is the inner side of a letter but currently, it draws contours when it's not the first or last shape) But the sketch throws errors even when the contour function was not used (fill(); was used).
2) thought it had something to do with the length of the curve, so tried to add a if statement to the part where it draws the letters. So far, I've tried using the width of the RGroup generated from the initial font size and string in void setup(){}, and the length of the bezier curve. The condition examples within the if statement was as follows;
-draw letters when the RGroup shape's width is smaller than the length of the curve
-draw letters when the "indent"(a variable to calculate the position on the curve) value is smaller than the length of the curve. (this case made the sketch to draw letters only when the letters were placed within the curve, but the error still occurred)
-draw letters when the "indent"(a variable to calculate the position on the curve) value is smaller than the width of the RGroup.
I've failed to see where exactly the problem is occurring, so I'm sharing the entire code within the sketch, but I marked the spot where I presume the error is happening with "//*******".
This study was based on the following link.
The geomerative library documentation can be seen from the following link.
//build a bezier curve using the recursive polynomial form
//number of control points(num+1 points)
int num = 4;
//arraylist to store the picked values
ArrayList<Point> pt;
float nfac;
import geomerative.*;
RFont fnt;
RGroup rg;
RPoint [][]rp;
String str = "(O_o)/ Oooh";
FloatList X;
FloatList Y;
FloatList SUM;
void setup(){
size(1000,1000,P2D);
RG.init(this);
pt = new ArrayList<Point>();
//pick a number of points with random positions and store x,y values in them
for(int i=0; i<=num; i++){
float x = random(0,width);
float y = random(0,height);
pt.add(new Point(x,y));
}
fnt = new RFont("Zapfino.ttf",100);
//RCommand.setSegmentAngle(random(0,HALF_PI));
//RCommand.setSegmentator(RCommand.ADAPTATIVE);
RCommand.setSegmentLength(3);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
rg = fnt.toGroup(str);
rp = rg.getPointsInPaths();
X = new FloatList();
Y = new FloatList();
SUM = new FloatList();
}
void draw(){
background(255);
noFill();
strokeCap(ROUND);
strokeWeight(2);
//draw points
beginShape();
for(int i=0; i<=num; i++){
vertex(pt.get(i).x,pt.get(i).y);
}
endShape();
for(int i=0; i<=num; i++){
ellipse(pt.get(i).x,pt.get(i).y,10,10);
}
//draw curve
nfac = 1;
for(int i=0; i<num; i++){
nfac *= (i+1);
}
strokeWeight(2);
beginShape();
int Bzindex = 0;
for(float t=0; t<=1; t+=0.01){
float x = 0;
float y = 0;
Bzindex++;
for(int i=0; i<=num; i++){
float coef = 1;
float kfac = 1;
float k_nfac = 1;
for(int k=i; k>0; k--){
kfac *= k;
}
for(int k=(num-i); k>0; k--){
k_nfac *= k;
}
coef = nfac/(kfac*k_nfac);
x += coef*(pow((1-t),num-i)*pow(t,i)*pt.get(i).x);
y += coef*(pow((1-t),num-i)*pow(t,i)*pt.get(i).y);
}
vertex(x,y);
X.set(Bzindex,x);
Y.set(Bzindex,y);
}
endShape();
//get arclength by pulling points from a floatlist
int numberOfDivisions = X.size()-2;
int maxPoint = numberOfDivisions+1;
float sum = 0;
float prevPointX = X.get(0);
float prevPointY = Y.get(0);
for(int i=1; i<=maxPoint; i++){
float pointX = X.get(i);
float pointY = Y.get(i);
sum += dist(pointX,pointY,prevPointX,prevPointY);
SUM.set(i-1,sum);
prevPointX = pointX;
prevPointY = pointY;
}
//
//*******************************************************
noFill();
//creates error only when using fill();
fill(0,255,0);
stroke(0);
//noStroke();
strokeWeight(4);
float indent = SUM.get(0);
beginShape();
for(int i=0; i<rp.length; i++){
if(i>0){
beginContour();
}
for(int j=0; j<rp[i].length; j++){
float t = 0;
indent = rp[i][j].x+SUM.get(0);
//
float targetArcLength = indent;
int index = IndexOfLargestValueSmallerThan(targetArcLength);
if(SUM.get(index)==targetArcLength){
t = index/(SUM.size()-1);
}else{
float lengthBefore = SUM.get(index);
float lengthAfter = SUM.get(index+1);
float segmentLength = lengthAfter - lengthBefore;
float segmentFraction = (targetArcLength - lengthBefore)/segmentLength;
t = (index+segmentFraction)/(SUM.size()-1);
}
float x = 0;
float y = 0;
float vx = 0;
float vy = 0;
for(int l=0; l<=num; l++){
float coef = 1;
float kfac = 1;
float k_nfac = 1;
for(int k=l; k>0; k--){
kfac *= k;
}
for(int k=(num-l); k>0; k--){
k_nfac *= k;
}
coef = nfac/(kfac*k_nfac);
x += coef*(pow((1-t),num-l)*pow(t,l)*pt.get(l).x);
y += coef*(pow((1-t),num-l)*pow(t,l)*pt.get(l).y);
float v = (pow(1-t,num-l)*l*pow(t,l-1))+((num-l)*pow(1-t,num-l-1)*(-1)*pow(t,l));
vx += coef*pt.get(l).x*(v);
vy += coef*pt.get(l).y*(v);
}
PVector P = new PVector(x,rp[i][j].y+y);
PVector ldir = new PVector(P.x-x,P.y-y);
PVector dir = new PVector(vy,-vx);
//
ldir.rotate(dir.heading()+PI/2);
vertex(x+ldir.x,y+ldir.y);
}
if(i<rp.length&&i>0){
endContour();
}
}
endShape();
//**************************************************************
}
int IndexOfLargestValueSmallerThan(float _targetArcLength){
int index = 0;
for(int i=0; i<SUM.size()-1; i++){
if(SUM.get(i)<=_targetArcLength){
index = i;
}
}
return index;
}
void mouseDragged(){
int which = -1;
if((mouseX<width)&&(mouseX>0)&&(mouseY<height)&&(mouseY>0)){
for(int i=0; i<=num; i++){
if(dist(mouseX,mouseY,pt.get(i).x,pt.get(i).y)<50){
which = i;
pt.get(which).update(mouseX,mouseY);
}
}
}
}
class Point{
float x,y;
Point(float _x, float _y){
x = _x;
y = _y;
}
void update(float _newx, float _newy){
x = _newx;
y = _newy;
}
}
Sometimes the error happens when the sketch is loaded. Most of the time, it loads okay but throws an error when you drag the point around a bit. The error code sometimes refers to the point where the control points of the curve are updated by mouse position, but because error somtimes occurs when the sketch is loaded as well, I didn't think it was a problem connected to the updated positions.
The error code is as follows;
a.lang.AssertionError
at processing.opengl.PSurfaceJOGL$2.run(PSurfaceJOGL.java:412)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.AssertionError
at jogamp.opengl.glu.tessellator.Sweep.AddRightEdges(Sweep.java:358)
at jogamp.opengl.glu.tessellator.Sweep.SweepEvent(Sweep.java:1105)
at jogamp.opengl.glu.tessellator.Sweep.__gl_computeInterior(Sweep.java:1339)
at jogamp.opengl.glu.tessellator.GLUtessellatorImpl.gluTessEndPolygon(GLUtessellatorImpl.java:526)
at com.jogamp.opengl.glu.GLU.gluTessEndPolygon(GLU.java:896)
at processing.opengl.PJOGL$Tessellator.endPolygon(PJOGL.java:641)
at processing.opengl.PGraphicsOpenGL$Tessellator.tessellatePolygon(PGraphicsOpenGL.java:12621)
at processing.opengl.PGraphicsOpenGL.tessellate(PGraphicsOpenGL.java:2255)
at processing.opengl.PGraphicsOpenGL.endShape(PGraphicsOpenGL.java:1965)
at processing.core.PGraphics.endShape(PGraphics.java:1707)
at processing.core.PApplet.endShape(PApplet.java:11641)
at bezier_polynomial_recursive_text_03.draw(bezier_polynomial_recursive_text_03.java:218)
at processing.core.PApplet.handleDraw(PApplet.java:2475)
at processing.opengl.PSurfaceJOGL$DrawListener.display(PSurfaceJOGL.java:866)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:692)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:674)
RuntimeException: java.lang.AssertionError
at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:443)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1293)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:1147)
at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:759)
at com.jogamp.opengl.util.AWTAnimatorImpl.display(AWTAnimatorImpl.java:81)
at com.jogamp.opengl.util.AnimatorBase.display(AnimatorBase.java:452)
at com.jogamp.opengl.util.FPSAnimator$MainTask.run(FPSAnimator.java:178)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
I don't think this would be a direct answer to my question, but it did stop the errors from occurring while using both fill() and the P2D renderer. The main problem, as pointed out by laancelot above, indeed seems to have been connected with stack overflow. So I approached the problem in two ways written below; Conclusion: The direct reason was a poorly expressed math formula.
1) switching the RPoints inside a class.
-I don't think this was the direct reason the errors were occurring, because at the stage where only this part of rewriting the code was done, the errors were still there. But maybe it was part of the problem. I'm not sure.
2) rewriting the part where the code expresses the formula to evaluate bezier curves at a specific point.
-Previously, the formula was made by using the explicit definition of a bezier curve with degree n. And, as a result, the formula had to be calculated(more like made) for every point in the RPoint points. As is mentioned on the wikipedia page about bezier curves, this way of computing is not recommended.
-on the revised code, the formula used to warp text was expressed in the polynomial form. Thus, it was able to pre-calculate the coefficients of the polynomial before the RPoint points were iterated. This seems to have solved the problem.
I'm still not really confident about what actually caused the problem and why it has been solved, and which part of the code I should show to explain this to others, so I'll share the entire code that has been rewritten. You need processing, the geomerative library, and a ttf type font file in a data folder to test the code. I have marked the place where the revised version of the formula is implicated. (It's still really messy....)
//n number of points
int num = 4;
//arraylist to store the picked values
ArrayList<cntrlPoint> pt;
//import the geomerative library
import geomerative.*;
//string
String str = "(O_o)/ Oooh";
FloatList X;
FloatList Y;
FloatList SUM;
RClass rc;
void setup() {
size(1000, 1000, P2D);
pt = new ArrayList<cntrlPoint>();
//pick a number of points with random positions
for (int i=0; i<=num; i++) {
float x = random(0, width);
float y = random(0, height);
pt.add(new cntrlPoint(x, y));
}
RG.init(this);
rc = new RClass();
X = new FloatList();
Y = new FloatList();
SUM = new FloatList();
}
void draw() {
background(255);
noFill();
strokeWeight(2);
drwCntrlPoints();
drwCurve();
gtArcLength();
fill(0,255,0);
rc.crtPoly(pt);
rc.drwText();
}
void drwCntrlPoints() {
//draw points
beginShape();
for (int i=0; i<=num; i++) {
vertex(pt.get(i).x, pt.get(i).y);
}
endShape();
for (int i=0; i<=num; i++) {
ellipse(pt.get(i).x, pt.get(i).y, 10, 10);
}
}
void drwCurve() {
//draw curve
float curveDetail = 0.01;
float nfac = 1;
for (int i=0; i<num; i++) {
nfac *= (i+1);
}
int arcIndex = 0;
strokeWeight(2);
beginShape();
for (float t=0; t<=1; t+=curveDetail) {
float x = 0;
float y = 0;
arcIndex++;
for (int i=0; i<=num; i++) {
float coef = 1;
float kfac = 1;
float k_nfac = 1;
for (int k=i; k>0; k--) {
kfac *= k;
}
for (int k=(num-i); k>0; k--) {
k_nfac *= k;
}
coef = nfac/(kfac*k_nfac);
x += coef*(pow((1-t), num-i)*pow(t, i)*pt.get(i).x);
y += coef*(pow((1-t), num-i)*pow(t, i)*pt.get(i).y);
}
vertex(x, y);
X.set(arcIndex, x);
Y.set(arcIndex, y);
}
endShape();
}
void gtArcLength() {
//get arclength by pulling points from a floatlist
int numberOfDivisions = X.size()-2;
int maxPoint = numberOfDivisions+1;
float sum = 0;
float prevPointX = X.get(0);
float prevPointY = Y.get(0);
for (int i=1; i<=maxPoint; i++) {
float pointX = X.get(i);
float pointY = Y.get(i);
sum += dist(pointX, pointY, prevPointX, prevPointY);
SUM.set(i-1, sum);
prevPointX = pointX;
prevPointY = pointY;
}
}
//*******factorial
int fact(int fa){
if(fa==1){
return 1;
}
if(fa==0){
return 1;
}
else{
return fa*fact(fa-1);
}
}
//********************
int IndexOfLargestValueSmallerThan(float _targetArcLength) {
int index = 0;
for (int i=0; i<SUM.size()-1; i++) {
if (SUM.get(i)<=_targetArcLength) {
index = i;
}
}
return index;
}
void mouseDragged() {
int which = -1;
if ((mouseX<width)&&(mouseX>0)&&(mouseY<height)&&(mouseY>0)) {
for (int i=0; i<=num; i++) {
if (dist(mouseX, mouseY, pt.get(i).x, pt.get(i).y)<80) {
which = i;
}
}
if (which>-1) {
pt.get(which).update(mouseX, mouseY);
}
}
}
class RClass {
//get ttf file
//create rfont
RFont fnt;
//turn rfont to rgroup to get points
RGroup rg;
//going to get point in path, so that the characters in the string can be seperated
RPoint [][]rp;
//floatlist to store coefficients
FloatList Cx;
FloatList Cy;
RClass() {
fnt = new RFont("Zapfino.ttf", 100);
rg = fnt.toGroup(str);
rp = rg.getPointsInPaths();
//RCommand.setSegmentAngle(random(0,HALF_PI));
//RCommand.setSegmentator(RCommand.ADAPTATIVE);
RCommand.setSegmentLength(3);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
Cx = new FloatList();
Cy = new FloatList();
}
//**********************************here
void crtPoly(ArrayList<cntrlPoint> _pt){
float ptsize = _pt.size();
for(int j=0; j<ptsize; j++){
float coefx = 0;
float coefy = 0;
float pi = 1;
float sigx = 0;
float sigy = 0;
for(int m=0; m<=j-1; m++){
pi *= (ptsize-1-m);
}
for(int i=0; i<=j; i++){
sigx += (pow(-1,i+j)*pt.get(i).x)/(fact(i)*fact(j-i));
sigy += (pow(-1,i+j)*pt.get(i).y)/(fact(i)*fact(j-i));
}
coefx = pi*sigx;
coefy = pi*sigy;
Cx.set(j,coefx);
Cy.set(j,coefy);
}
}
//**************************************
void drwText() {
float indent = SUM.get(0);
beginShape();
for (int i=0; i<rp.length; i++) {
if(i>0){
beginContour();
}
for (int j=0; j<rp[i].length; j++) {
float t = 0;
indent = rp[i][j].x+SUM.get(0);
float targetArcLength = indent;
int index = IndexOfLargestValueSmallerThan(targetArcLength);
if (SUM.get(index)==targetArcLength) {
t = index/(SUM.size()-1);
} else {
float lengthBefore = SUM.get(index);
float lengthAfter = SUM.get(index+1);
float segmentLength = lengthAfter - lengthBefore;
float segmentFraction = (targetArcLength - lengthBefore)/segmentLength;
t = (index+segmentFraction)/(SUM.size()-1);
}
//***************************here
float x = 0;
float y = 0;
float vx = 0;
float vy = 0;
for(int l=0; l<=num; l++){
x += Cx.get(l)*pow(t,l);
y += Cy.get(l)*pow(t,l);
}
for(int l=1; l<=num; l++){
vx += l*Cx.get(l)*pow(t,l-1);
vy += l*Cy.get(l)*pow(t,l-1);
}
//**************************************
PVector P = new PVector(x, rp[i][j].y+y);
PVector ldir = new PVector(P.x-x, P.y-y);
PVector dir = new PVector(vy, -vx);
//
ldir.rotate(dir.heading()+PI/2);
vertex(x+ldir.x, y+ldir.y);
}
if(i>0&&i<rp.length){
endContour();
}
}
endShape();
}
}
class cntrlPoint{
float x,y;
cntrlPoint(float _x, float _y){
x = _x;
y = _y;
}
void update(float _newx, float _newy){
x = _newx;
y = _newy;
}
}
I have a homework task where I have to write a class responsible for contour detection. It is essentially an image processing operation, using the definition of euclidean distance between 2 points in the 3-dimensional space. Formula given to us to use is:
Math.sqrt(Math.pow(pix1.red - pix2.red,2) + Math.pow(pix1.green- pix2.green,2) + Math.pow(pix1.blue- pix2.blue,2));
We need to consider each entry of the two dimensional array storing the colors of the pixels of an image, and if some pixel, pix, the color distance between p and any of its neighbors is more than 70, change the color of the pixel to black, else change it to white.
We are given a seperate class as well responsible for choosing an image, and selecting an output, for which method operationContouring is applied to. Java syntax and convention is very new to me having started with python. Conceptually, I'm struggling to understand what the difference between pix1 and pix2 is, and how to define them. This is my code so far.
Given:
import java.awt.Color;
/* Interface for ensuring all image operations invoked in same manner */
public interface operationImage {
public Color[][] operationDo(Color[][] imageArray);
}
My code:
import java.awt.Color;
public class operationContouring implements operationImage {
public Color[][] operationDo(Color[][] imageArray) {
int numberOfRows = imageArray.length;
int numberOfColumns = imageArray[0].length;
Color[][] results = new Color[numberOfRows][numberOfColumns];
for (int i = 0; i < numberOfRows; i++)
for (int j = 0; j < numberOfColumns; j++) {
int red = imageArray[i][j].getRed();
int green = imageArray[i][j].getGreen();
int blue = imageArray[i][j].getBlue();
double DistanceColor = Math.sqrt(Math.pow(pix1.red - pix2.red,2) + Math.pow(pix1.green- pix2.green,2) + Math.pow(pix1.blue- pix2.blue,2));
int LIMIT = 70;
if (DistanceColor> LIMIT ) {
results[i][j] = new Color((red=0), (green=0), (blue=0));
}
else {
results[i][j] = new Color((red=255), (green=255), (blue=255));
}
}
return results;
}
}
This is a solution I wrote that uses BufferedImages. I tested it and it should work. Try changing it such that it uses your data format (Color[][]) and it should work for you too. Note that "pix1" is nothing more than a description of the color of some pixel, and "pix2" is the description of the color of the pixel you are comparing it to (determining whether the color distance > 70).
public static boolean tooDifferent(Color c1, Color c2) {
return Math.sqrt(Math.pow(c1.getRed() - c2.getRed(),2) + Math.pow(c1.getGreen()- c2.getGreen(),2) + Math.pow(c1.getBlue()- c2.getBlue(),2)) > 70;
}
public static Color getColor(int x, int y, BufferedImage img) {
return new Color(img.getRGB(x, y));
}
public static BufferedImage operationDo(BufferedImage img) {
int numberOfRows = img.getHeight();
int numberOfColumns = img.getWidth();
BufferedImage results = new BufferedImage(numberOfColumns, numberOfRows, BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < numberOfRows; y++) {
for (int x = 0; x < numberOfColumns; x++) {
Color color = new Color(img.getRGB(x, y));
boolean aboveExists = y > 0;
boolean belowExists = y < numberOfRows - 1;
boolean leftExists = x > 0;
boolean rightExists = x < numberOfColumns - 1;
if ((aboveExists && tooDifferent(color, getColor(x, y - 1, img))) ||
(belowExists && tooDifferent(color, getColor(x, y + 1, img))) ||
(leftExists && tooDifferent(color, getColor(x - 1, y, img))) ||
(rightExists && tooDifferent(color, getColor(x + 1, y, img)))) {
results.setRGB(x, y, Color.black.getRGB());
} else {
results.setRGB(x, y, Color.white.getRGB());
}
}
}
return results;
}
I've created a function to generate a 2-dimension array of Perlin Noise data, starting with a base noise array of random values between 0 and 1.
The base white noise array looks like this:
From there, I use the smoothNoise() function to interpolate values to create good looking image (which I don't have because it doesn't work)
The smooth noise function is called once every octave. The images of what data the smooth noise function returns is as follows (octaves lowest to highest)
After all the smoothing is done the image outputted turns black. I would upload an image but it is just black so there is no need.
My Code is:
import java.util.Random;
public class Noise2D {
int width;
int height;
public Noise2D(int width, int height){
this.width = width;
this.height = height;
}
public double[][] generateWhiteNoise(int width,int height){
Random r = new Random(0);
double[][] whiteNoise = new double[width][height];
for(int i = 0; i<whiteNoise.length; i++){
for(int j = 0; j<whiteNoise[0].length;j++){
double rNum = r.nextDouble()%1;
whiteNoise[i][j] = (double)rNum;
}
}
ImageWriter.writeImage(whiteNoise, "WhiteNoise");
return whiteNoise;
}
public double interpolate(double x0, double x1, double alpha){
return x0 * (1 - alpha) + alpha * x1;
}
public double[][] generateSmoothNoise(double[][] baseNoise, int octave){
int width = baseNoise.length;
int height = baseNoise[0].length;
double[][] smoothNoise = new double[width][height];
int period = 1<< octave; //2^i
double frequency = 1.0/period;
for(int x = 0; x<width; x++){
int x0 = (x/period)*period; //7/3 = 2 *3 = 6
int x1 = (x0+period)%width;
double hBlend = (x-x0)*frequency;
for(int y = 0; y<height; y++){
int y0 = (y/period)*period;
int y1 = (y0 + period)%height;
double vBlend = (y - y0)*frequency;
double top = interpolate(baseNoise[x0][y0],baseNoise[x1][y1],hBlend);
double bottom = interpolate(baseNoise[x0][y1],baseNoise[x1][y0],hBlend);
smoothNoise[x][y] = interpolate(top,bottom,vBlend);
}
}
ImageWriter.writeImage(smoothNoise,"Smooth"+Integer.toString(octave));
return smoothNoise;
}
public double[][] generatePerlinNoise(double baseNoise[][], int octaves){
int width = baseNoise.length;
int height = baseNoise[0].length;
double persistence = 0.5;
double[][][] smoothNoise = new double[octaves][][];
for(int i = 0; i<octaves; i++){
smoothNoise[i] = generateSmoothNoise(baseNoise,i);
}
double[][] perlinNoise = new double[width][height];
double amplitude = 1;
double totalAmplitude = 0;
for(int octave = octaves-1; octave>=0; octave--){
amplitude*=persistence;
totalAmplitude+=amplitude;
for(int x = 0; x<width;x++){
for(int y = 0; y<height; y++){
perlinNoise[x][y] = smoothNoise[octave][x][y] * amplitude;
}
}
}
ImageWriter.writeImage(perlinNoise,"files");
for(int i = 0; i<width; i++){
for(int j = 0; j<height; j++){
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
}
The Class is defined and methods are called by:
Perlin p = new Perlin(256,256);
writeImage(p.smoothNoise(p.makeNoise(256,256), 1, 16), "Perlin");
1 is frequency, 16 is octave count
Where the data is written in the function writeImage(2d array, "name") using the following code (I don't think there is a problem with this part but I'll post it anyway):
public static void writeImage(double[][] data,String name){
BufferedImage img = new BufferedImage(data.length,data[0].length,BufferedImage.TYPE_INT_RGB);
for(int y = 0; y<data.length; y++){
for(int x = 0; x<data[y].length; x++){
if(data[y][x]>1){
data[y][x] = 1;
}
if(data[y][x]<0){
data[y][x] = 0;
}
Color c = new Color((float)data[y][x],(float)data[y][x],(float)data[y][x]);
img.setRGB(x,y,c.getRGB());
}
}
try{
File file = new File(name+".png");
file.createNewFile();
ImageIO.write(img,"png",file);
}catch(IOException e){
e.printStackTrace();
}
}
public static void writeExistingImage(BufferedImage img){
try{
File file = new File("noise2.png");
file.createNewFile();
ImageIO.write(img,"png",file);
}catch(IOException e){
e.printStackTrace();
}
}
To conclude, I believe the problem lies within the smoothNoise method, although I could quite possibly be wrong because my knowledge of Perlin Noise is not extensive. If the problem does not lie here, I assume it lies in the generatePerlinNoise() method. Any help would be greatly appreciated. I will be so happy with any suggestion, I have been trying to solve this problem for an extremely long time.
To clarify:
My problem is that the generatePerlinNoise method returns a set of data(2d) array that makes a black image (rather than a cool noise image)
I also think that the smooth noise images are not supposed to look like they are split up into squares