I didn't find any examples in openCV to convert a flat image to cylindrical in java, I want it to render the image in 2d not 3d, also didn’t find any example code or book on it. Below is the image of pictures which I want to warp around a cup.
A good book and example code will be much appreciated.
This i have done so far. suggested my #Amitay to make image concave, using this example Wrap image around cylinder but stuck on convertion.
import java.io.File;
import org.bytedeco.javacpp.indexer.UByteBufferIndexer;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_highgui.imshow;
import static org.bytedeco.javacpp.opencv_highgui.waitKey;
import static org.bytedeco.javacpp.opencv_imgcodecs.CV_LOAD_IMAGE_COLOR;
import static org.bytedeco.javacpp.opencv_imgcodecs.imread;
/**
*
* #author BTACTC
*/
public class CupWrapping {
Mat image;
Mat dstImage;
int width;
int height;
public CupWrapping(File imageFile) {
image = imread(imageFile.getAbsolutePath(), CV_LOAD_IMAGE_COLOR);
width = image.size().width();
height = image.size().height();
dstImage = new Mat(width, height, image.type());
UByteBufferIndexer sI = image.createIndexer();
UByteBufferIndexer sD = dstImage.createIndexer();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Point2f current_pos = new Point2f(x, y);
current_pos = convert_pt(current_pos, width, height);
Point top_left = new Point((int) current_pos.x(), (int) current_pos.y()); //top left because of integer rounding
//make sure the point is actually inside the original image
if (top_left.x() < 0
|| top_left.x() > width - 2
|| top_left.y() < 0
|| top_left.y() > height - 2) {
continue;
}
//bilinear interpolation
float dx = current_pos.x() - top_left.x();
float dy = current_pos.y() - top_left.y();
float weight_tl = (float) ((1.0 - dx) * (1.0 - dy));
float weight_tr = (float) ((dx) * (1.0 - dy));
float weight_bl = (float) ((1.0 - dx) * (dy));
float weight_br = (dx) * (dy);
byte value = (byte) (weight_tl * sI.get(top_left.y(), top_left.x())
+ weight_tr * sI.get(top_left.y(), top_left.x() + 1)
+ weight_bl * sI.get(top_left.y() + 1, top_left.x())
+ weight_br * sI.get(top_left.y() + 1, top_left.x() + 1));
sD.put(y, x,value);
}
}
imshow("", dstImage);
waitKey(0);
}
public Point2f convert_pt(Point2f point, int w, int h) {
//center the point at 0,0
Point2f pc = new Point2f(point.x() - w / 2, point.y() - h / 2);
//these are your free parameters
float f = w;
float r = w;
float omega = w / 2;
float z0 = (float) (f - Math.sqrt(r * r - omega * omega));
float zc = (float) ((2 * z0 - Math.sqrt(4 * z0 * z0 - 4 * (pc.x() * pc.x() / (f * f) + 1) * (z0 * z0 - r * r))) / (2 * (pc.x() * pc.x() / (f * f) + 1)));
Point2f final_point = new Point2f(pc.x() * zc / f, pc.y() * zc / f);
final_point.x() = final_point.x() + w / 2;
final_point.y() += h / 2;
return final_point;
}
public static void main(String[] args) {
File imageFile = new File("image/C13.jpg");
CupWrapping wrap = new CupWrapping(imageFile);
}
}
Look at this answer in Warp Image to Appear in Cylindrical Projection
You only need to change two things:
Because you want convex projection and not concave you need to change the line of code in the function convert_pt.
From:
float zc = (2*z0+sqrt(4*z0*z0-4*(pc.x*pc.x/(f*f)+1)*(z0*z0-r*r)))/(2* (pc.x*pc.x/(f*f)+1));
To
float zc = (2*z0-sqrt(4*z0*z0-4*(pc.x*pc.x/(f*f)+1)*(z0*z0-r*r)))/(2* (pc.x*pc.x/(f*f)+1));
Convert the all the rest of the code form c++ to java.
Good luck
Related
I have to points in 3D space that I want to draw a cylinder between. I currently have this code:
applet.pushMatrix();
applet.stroke(0);
applet.fill(0);
applet.line(this.start.x, this.start.y, this.start.z, this.end.x, this.end.y, this.end.z); //debug, shows where the cylinder should be
applet.translate(this.start.x, this.start.y);
applet.beginShape(PConstants.TRIANGLE_STRIP);
float xdif = this.end.x - this.start.x;
float ydif = this.end.y - this.start.y;
float zdif = this.end.z - this.start.z;
float rx = (float)Math.atan(ydif / xdif);
float ry = (float)Math.atan(zdif / xdif);
float rz = (float)Math.atan(zdif == 0 ? 0 : ydif / zdif);
applet.rotateZ((float) (rx - Math.PI / 2));
applet.rotateY((float) (ry - Math.PI / 2));
applet.rotateX((float) (rz - Math.PI / 2));
float r = 20;
float len = HelperFunctions.distance(this.start, this.end) / 2;
boolean w = false;
for (int i = 0; i < 2; i += 1) {
for (float j = 0; j < Math.PI * 2 + 0.2; j += 0.4) {
w = !w;
float z = (i + (w ? 0 : 1)) * len;
float x = (float)(r * Math.cos(j));
float y = (float)(r * Math.sin(j));
applet.vertex(y, x, z);
}
}
applet.endShape();
applet.popMatrix();
The cylinder does draw correctly, however, it doesn't rotate correctly. I'm trying to use trig to determine the rotation angles, however, I'm not sure I've done it correctly. How would I get the correct angles to rotate around such that it is drawn from Vector start to Vector end?
Vector is a custom class with just a float x, y, z, and HelperFunctions.distance takes two Vectors and calculates the distance (pythagoras).
Thanks in advance.
I'm trying to make a 3d game with java from scratch but a have a problem with rendering a triangle after I multiply each vertices with projection matrix
I already try to use the projected vertices x and y but the result is all the vertices the in the same X, so I try to rotate the triangle X or Y or Z axis but the result is the same.
The result of rendering (draw in paint):
I know that the triangle is align with the camera but I tried to move the vertex that is overlapping out by changing its X or Y or Z coordinate but It didn't work
import java.awt.Color;
import java.awt.Graphics;
import measurement.MatrixF;
import measurement.Vector3f;
import model.Mesh;
import model.Triangle;
import toolbox.GE;
import toolbox.Matrix;
import toolbox.Vector;
public class MeshRenderer {
private int width, height;
private float fNear, fFar;
private float fov;
private float fAspectRatio;
private float fovRad;
private float theta;
private MatrixF projectionMatrix;
private MatrixF rotXMatrix;
private MatrixF rotYMatrix;
private MatrixF rotZMatrix;
private Vector3f globalTranslation;
public MeshRenderer(float fNear, float fFar, float fov, int width, int height) {
this.fNear = fNear;
this.fFar = fFar;
this.fov = fov;
this.fAspectRatio = height / width;
this.width = width;
this.height = height;
this.fovRad = (float) (1.0f / Math.tan(Math.toRadians(fov / 2)));
projectionMatrix = new MatrixF(4, 4);
rotXMatrix = new MatrixF(4, 4);
rotYMatrix = new MatrixF(4, 4);
rotZMatrix = new MatrixF(4, 4);
projectionMatrix.m[0][0] = fAspectRatio * fovRad;
projectionMatrix.m[1][1] = fovRad;
projectionMatrix.m[2][2] = (-(fFar + fNear)) / (fFar - fNear);
projectionMatrix.m[3][2] = (-2 * fFar * fNear) / (fFar - fNear);
projectionMatrix.m[2][3] = -1.0f;
projectionMatrix.m[3][3] = 0.0f;
rotXMatrix.m[0][0] = 1;
rotXMatrix.m[1][1] = (float) Math.cos(theta);
rotXMatrix.m[2][1] = (float) -Math.sin(theta);
rotXMatrix.m[1][2] = (float) Math.sin(theta);
rotXMatrix.m[2][2] = (float) Math.cos(theta);
rotYMatrix.m[0][0] = (float) Math.cos(theta);
rotYMatrix.m[2][0] = (float) Math.sin(theta);
rotYMatrix.m[1][1] = (float) 1.0;
rotYMatrix.m[0][2] = (float) -Math.sin(theta);
rotYMatrix.m[2][2] = (float) Math.cos(theta);
rotXMatrix.m[2][2] = 1;
rotXMatrix.m[0][0] = (float) Math.cos(theta);
rotXMatrix.m[1][0] = (float) -Math.sin(theta);
rotXMatrix.m[0][1] = (float) Math.sin(theta);
rotXMatrix.m[1][1] = (float) Math.cos(theta);
//projectionMatrix = Matrix.transpose(projectionMatrix);
globalTranslation = new Vector3f(0.0f, 0.0f, 0.0f);
}
public void renderMesh(Mesh mesh, Graphics g) {
for(int i = 0; i < mesh.tris.length; i++) {
Triangle tri = new Triangle(mesh.tris[i].p[0], mesh.tris[i].p[1], mesh.tris[i].p[2]);
Triangle translatedTri = tri;
Triangle projectedTri = new Triangle();
theta += 0.0001;
this.calculateRotationMatrix(theta);
translatedTri.p[0] = Matrix.multiplyMatrixVector(tri.p[0], rotYMatrix);
translatedTri.p[1] = Matrix.multiplyMatrixVector(tri.p[1], rotYMatrix);
translatedTri.p[2] = Matrix.multiplyMatrixVector(tri.p[2], rotYMatrix);
translatedTri.p[0].z = tri.p[0].z + globalTranslation.z;
translatedTri.p[1].z = tri.p[1].z + globalTranslation.z;
translatedTri.p[2].z = tri.p[2].z + globalTranslation.z;
projectedTri.p[0] = Matrix.multiplyMatrixVector(translatedTri.p[0], projectionMatrix);
projectedTri.p[1] = Matrix.multiplyMatrixVector(translatedTri.p[1], projectionMatrix);
projectedTri.p[2] = Matrix.multiplyMatrixVector(translatedTri.p[2], projectionMatrix);
projectedTri.p[0].x += 1.0f; projectedTri.p[0].y += 1.0f;
projectedTri.p[1].x += 1.0f; projectedTri.p[1].y += 1.0f;
projectedTri.p[2].x += 1.0f; projectedTri.p[2].y += 1.0f;
float scale = 0.5f;
projectedTri.p[0].x *= scale * width;
projectedTri.p[0].y *= scale * height;
projectedTri.p[1].x *= scale * width;
projectedTri.p[1].y *= scale * height;
projectedTri.p[2].x *= scale * width;
projectedTri.p[2].y *= scale * height;
GE.drawTriangle(projectedTri.p[0].x, projectedTri.p[0].y, projectedTri.p[1].x, projectedTri.p[1].y, projectedTri.p[2].x, projectedTri.p[2].y, Color.WHITE, g);
for(int j = 0; j < projectedTri.p.length; j++) {
g.setColor(new Color(255, 0, (j * 50)));
g.fillRect((int)projectedTri.p[j].x - 8, (int)projectedTri.p[j].y - 8, 16 - j, 16 - j);
}
translatedTri.p[0].z = tri.p[0].z - globalTranslation.z;
translatedTri.p[1].z = tri.p[1].z - globalTranslation.z;
translatedTri.p[2].z = tri.p[2].z - globalTranslation.z;
}
}
private void calculateRotationMatrix(float theta) {
rotXMatrix.m[0][0] = 1;
rotXMatrix.m[1][1] = (float) Math.cos(theta);
rotXMatrix.m[2][1] = (float) -Math.sin(theta);
rotXMatrix.m[1][2] = (float) Math.sin(theta);
rotXMatrix.m[2][2] = (float) Math.cos(theta);
rotYMatrix.m[0][0] = (float) Math.cos(theta);
rotYMatrix.m[2][0] = (float) Math.sin(theta);
rotYMatrix.m[1][1] = (float) 1.0;
rotYMatrix.m[0][2] = (float) -Math.sin(theta);
rotYMatrix.m[2][2] = (float) Math.cos(theta);
rotXMatrix.m[2][2] = 1;
rotXMatrix.m[0][0] = (float) Math.cos(theta);
rotXMatrix.m[1][0] = (float) -Math.sin(theta);
rotXMatrix.m[0][1] = (float) Math.sin(theta);
rotXMatrix.m[1][1] = (float) Math.cos(theta);
}
public Vector3f getTranslation() {
return globalTranslation;
}
public float getfNear() {
return fNear;
}
public float getfFar() {
return fFar;
}
public float getFov() {
return fov;
}
public float getfAspectRatio() {
return fAspectRatio;
}
public float getFovRad() {
return fovRad;
}
}
The matrix (4x4) multiply with vector3 function just in case:
Vector3f o = new Vector3f(0, 0, 0);
o.x = (i.x * m.m[0][0]) + (i.y * m.m[1][0]) + (i.z * m.m[2][0]) + m.m[3][0];
o.y = (i.x * m.m[0][1]) + (i.y * m.m[1][1]) + (i.z * m.m[2][1]) + m.m[3][1];
o.z = (i.x * m.m[0][2]) + (i.y * m.m[1][2]) + (i.z * m.m[2][2]) + m.m[3][2];
float w = (i.x * m.m[0][3]) + (i.y * m.m[1][3]) + (i.z * m.m[2][3]) + m.m[3][3];
if (w != 0.0f)
{
o.x /= w; o.y /= w; o.z /= w;
}
return o;
}
Without seeing exactly how this class is being used, it's hard to say exactly what the problem is, but FWIW I'm not seeing too much wrong with the math:
There are a couple places where you probably intended to initialize rotZMatrix instead of reinitializing rotXMatrix, but the code is not actually using either.
When adding in globalTranslation, you are overwriting the rotated z coordinate with the pre-rotated z coordinate, when you probably just want to update the rotated coordinate.
It's not apparent whether MatrixF is initialized to the identity or to zeros -- but if the latter, you probably should be populating the m[3][3] element of the rotation matrices with 1.0.
Naturally, you probably want to lift the theta increment and rotation calculation outside of the triangle loop, once you have more than one triangle.
I'm guessing that the problem is that you are leaving globalTranslation at zero and that the mesh is near the origin -- hence the transformed geometry is on the wrong side of the near plane and outside of the view frustum. Most graphic engines would cull such geometry, since the post-transform results will lie outside of clip space and will look increasingly anomalous around and behind the eye point.
I'd recommend trying to adjust globalTranslation.z to ensure 0 < fNear < translatedTri.p[i].z < fFar, for all the translated points.
(You could also try temporarily swapping the perspective matrix with a orthographic projection matrix, to determine whether the problem is in the projection/homogenization math or elsewhere.)
I'm trying to create a heightmap colored by face, instead of vertex. For example, this is what I currently have:
But this is what I want:
I read that I have to split each vertex into multiple vertices, then index each separately for the triangles. I also know that blender has a function like this for its models (split vertices, or something?), but I'm not sure what kind of algorithm I would follow for this. This would be the last resort, because multiplying the amount of vertices in the mesh for no reason other than color doesn't seem efficient.
I also discovered something called flatshading (using the flat qualifier on the pixel color in the shaders), but it seems to only draw squares instead of triangles. Is there a way to make it shade triangles?
For reference, this is my current heightmap generation code:
public class HeightMap extends GameModel {
private static final float START_X = -0.5f;
private static final float START_Z = -0.5f;
private static final float REFLECTANCE = .1f;
public HeightMap(float minY, float maxY, float persistence, int width, int height, float spikeness) {
super(createMesh(minY, maxY, persistence, width, height, spikeness), REFLECTANCE);
}
protected static Mesh createMesh(final float minY, final float maxY, final float persistence, final int width,
final int height, float spikeness) {
SimplexNoise noise = new SimplexNoise(128, persistence, 2);// Utils.getRandom().nextInt());
float xStep = Math.abs(START_X * 2) / (width - 1);
float zStep = Math.abs(START_Z * 2) / (height - 1);
List<Float> positions = new ArrayList<>();
List<Integer> indices = new ArrayList<>();
for (int z = 0; z < height; z++) {
for (int x = 0; x < width; x++) {
// scale from [-1, 1] to [minY, maxY]
float heightY = (float) ((noise.getNoise(x * xStep * spikeness, z * zStep * spikeness) + 1f) / 2
* (maxY - minY) + minY);
positions.add(START_X + x * xStep);
positions.add(heightY);
positions.add(START_Z + z * zStep);
// Create indices
if (x < width - 1 && z < height - 1) {
int leftTop = z * width + x;
int leftBottom = (z + 1) * width + x;
int rightBottom = (z + 1) * width + x + 1;
int rightTop = z * width + x + 1;
indices.add(leftTop);
indices.add(leftBottom);
indices.add(rightTop);
indices.add(rightTop);
indices.add(leftBottom);
indices.add(rightBottom);
}
}
}
float[] verticesArr = Utils.listToArray(positions);
Color c = new Color(147, 105, 59);
float[] colorArr = new float[positions.size()];
for (int i = 0; i < colorArr.length; i += 3) {
float brightness = (Utils.getRandom().nextFloat() - 0.5f) * 0.5f;
colorArr[i] = (float) c.getRed() / 255f + brightness;
colorArr[i + 1] = (float) c.getGreen() / 255f + brightness;
colorArr[i + 2] = (float) c.getBlue() / 255f + brightness;
}
int[] indicesArr = indices.stream().mapToInt((i) -> i).toArray();
float[] normalArr = calcNormals(verticesArr, width, height);
return new Mesh(verticesArr, colorArr, normalArr, indicesArr);
}
private static float[] calcNormals(float[] posArr, int width, int height) {
Vector3f v0 = new Vector3f();
Vector3f v1 = new Vector3f();
Vector3f v2 = new Vector3f();
Vector3f v3 = new Vector3f();
Vector3f v4 = new Vector3f();
Vector3f v12 = new Vector3f();
Vector3f v23 = new Vector3f();
Vector3f v34 = new Vector3f();
Vector3f v41 = new Vector3f();
List<Float> normals = new ArrayList<>();
Vector3f normal = new Vector3f();
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (row > 0 && row < height - 1 && col > 0 && col < width - 1) {
int i0 = row * width * 3 + col * 3;
v0.x = posArr[i0];
v0.y = posArr[i0 + 1];
v0.z = posArr[i0 + 2];
int i1 = row * width * 3 + (col - 1) * 3;
v1.x = posArr[i1];
v1.y = posArr[i1 + 1];
v1.z = posArr[i1 + 2];
v1 = v1.sub(v0);
int i2 = (row + 1) * width * 3 + col * 3;
v2.x = posArr[i2];
v2.y = posArr[i2 + 1];
v2.z = posArr[i2 + 2];
v2 = v2.sub(v0);
int i3 = (row) * width * 3 + (col + 1) * 3;
v3.x = posArr[i3];
v3.y = posArr[i3 + 1];
v3.z = posArr[i3 + 2];
v3 = v3.sub(v0);
int i4 = (row - 1) * width * 3 + col * 3;
v4.x = posArr[i4];
v4.y = posArr[i4 + 1];
v4.z = posArr[i4 + 2];
v4 = v4.sub(v0);
v1.cross(v2, v12);
v12.normalize();
v2.cross(v3, v23);
v23.normalize();
v3.cross(v4, v34);
v34.normalize();
v4.cross(v1, v41);
v41.normalize();
normal = v12.add(v23).add(v34).add(v41);
normal.normalize();
} else {
normal.x = 0;
normal.y = 1;
normal.z = 0;
}
normal.normalize();
normals.add(normal.x);
normals.add(normal.y);
normals.add(normal.z);
}
}
return Utils.listToArray(normals);
}
}
Edit
I've tried doing a couple things. I tried rearranging the indices with flat shading, but that didn't give me the look I wanted. I tried using a uniform vec3 colors and indexing it with gl_VertexID or gl_InstanceID (I'm not entirely sure the difference), but I couldn't get the arrays to compile.
Here is the github repo, by the way.
flat qualified fragment shader inputs will receive the same value for the same primitive. In your case, a triangle.
Of course, a triangle is composed of 3 vertices. And if the vertex shaders output 3 different values, how does the fragment shader know which value to get?
This comes down to what is called the "provoking vertex." When you render, you specify a particular primitive to use in your glDraw* call (GL_TRIANGLE_STRIP, GL_TRIANGLES, etc). These primitive types will generate a number of base primitives (ie: single triangle), based on how many vertices you provided.
When a base primitive is generated, one of the vertices in that base primitive is said to be the "provoking vertex". It is that vertex's data that is used for all flat parameters.
The reason you're seeing what you are seeing is because the two adjacent triangles just happen to be using the same provoking vertex. Your mesh is smooth, so two adjacent triangles share 2 vertices. Your mesh generation just so happens to be generating a mesh such that the provoking vertex for each triangle is shared between them. Which means that the two triangles will get the same flat value.
You will need to adjust your index list or otherwise alter your mesh generation so that this doesn't happen. Or you can just divide your mesh into individual triangles; that's probably much easier.
As a final resort, I just duplicated the vertices, and it seems to work. I haven't been able to profile it to see if it makes a big performance drop. I'd be open to any other suggestions!
for (int z = 0; z < height; z++) {
for (int x = 0; x < width; x++) {
// scale from [-1, 1] to [minY, maxY]
float heightY = (float) ((noise.getNoise(x * xStep * spikeness, z * zStep * spikeness) + 1f) / 2
* (maxY - minY) + minY);
positions.add(START_X + x * xStep);
positions.add(heightY);
positions.add(START_Z + z * zStep);
positions.add(START_X + x * xStep);
positions.add(heightY);
positions.add(START_Z + z * zStep);
}
}
for (int z = 0; z < height - 1; z++) {
for (int x = 0; x < width - 1; x++) {
int leftTop = z * width + x;
int leftBottom = (z + 1) * width + x;
int rightBottom = (z + 1) * width + x + 1;
int rightTop = z * width + x + 1;
indices.add(2 * leftTop);
indices.add(2 * leftBottom);
indices.add(2 * rightTop);
indices.add(2 * rightTop + 1);
indices.add(2 * leftBottom + 1);
indices.add(2 * rightBottom + 1);
}
}
I want to generate different colors for my line graphs:
I want to generate equally different colors (for human eye)
I want them to be the same luminance (not computed brightness)
(this rules out RGB and HSL, YIQ is close but has not perfectly uniform luminance)
Have you used any libraries (in Java) to handle uniform color scheme generation?
I've spent a few days on this issue already, so I'm hoping some of you had the same problem and solved it.
Thanks!
Edit: unfortunately, I cannot use java.awt packages on AppEngine (or anything that uses java.awt.Color).
Here's a little function I wrote just now from looking at the Wikipedia page http://en.wikipedia.org/wiki/SRGB_color_space
private int makeARGB(double Y, double x, double y) {
// Out of gamut colour
int rgb = 0xFF808080;
double X = Y * x / y;
double Z = Y * (1 - x - y) / y;
double rlin = +3.2046 * X + -1.5372 * Y + -0.4986 * Z;
double r = gamma(rlin);
int ir = (int) (r * 255.0);
if (ir >= 0 && ir < 256) {
double glin = -0.9689 * X + +1.8758 * Y + +0.0415 * Z;
double g = gamma(glin);
int ig = (int) (g * 255.0);
if (ig >= 0 && ig < 256) {
double blin = +0.0557 * X + -0.2040 * Y + +1.0570 * Z;
double b = gamma(blin);
int ib = (int) (b * 255.0);
if (ib >= 0 && ib < 256) {
rgb = 0xFF000000 + (ir << 16) + (ig << 8) + (ib << 0);
}
}
}
return rgb;
}
private double gamma(double l) {
if (l < 0.0031308) {
return l * 12.92;
} else {
return 1.055 * Math.pow(l, 1.0 / 2.4) - 0.055;
}
}
private BufferedImage createImage() {
BufferedImage bm = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB);
for (int ix = 0; ix < bm.getWidth(); ++ix) {
double astar = ((double) ix) / ((double) bm.getWidth());
for (int iy = 1; iy < bm.getHeight(); ++iy) {
double bstar = ((double) iy) / ((double) bm.getHeight());
int rgb = makeARGB(0.3, astar, bstar);
bm.setRGB(ix, iy, rgb);
}
}
return bm;
}
You pass in a luminance Y and colour coordinates x,y. x and y are nominally from 0..1 but a lot of that 'space' is not in the sRGB gamut so doesn't correspond to a displayable colour. Y is also 0..1, try 0.3..0.5 initially.
An example image:
I don't know anything about google app engine but is an ARGB integer the kind of colour specification you need?
I need to be able to load RGB pixel values at a certain resolution into Java. That resolution is small (~300x300).
Currently, I've been loading them like this:
File file = new File("...path...");
BufferedImage imsrc = ImageIO.read(file);
int width = imsrc.getWidth();
int height = imsrc.getHeight();
int[] data = new int[width * height];
imsrc.getRGB(0,0, width, height, data, 0, width);
and then downsizing it myself.
Sam asked for the down-sizing code, so here it is:
/**
* DownSize an image.
* This is NOT precise, and is noisy.
* However, this is fast and better than NearestNeighbor
* #param pixels - _RGB pixel values for the original image
* #param width - width of the original image
* #param newWidth - width of the new image
* #param newHeight - height of the new image
* #return - _RGB pixel values of the resized image
*/
public static int[] downSize(int[] pixels, int width, int newWidth, int newHeight) {
int height = pixels.length / width;
if (newWidth == width && height == newHeight) return pixels;
int[] resized = new int[newWidth * newHeight];
float x_ratio = (float) width / newWidth;
float y_ratio = (float) height / newHeight;
float xhr = x_ratio / 2;
float yhr = y_ratio / 2;
int i, j, k, l, m;
for (int x = 0; x < newWidth; x ++)
for (int y = 0; y < newHeight; y ++) {
i = (int) (x * x_ratio);
j = (int) (y * y_ratio);
k = (int) (x * x_ratio + xhr);
l = (int) (y * y_ratio + yhr);
for (int p = 0; p < 3; p ++) {
m = 0xFF << (p * 8);
resized[x + y * newWidth] |= (
(pixels[i + j * width] & m) +
(pixels[k + j * width] & m) +
(pixels[i + l * width] & m) +
(pixels[k + l * width] & m) >> 2) & m;
}
}
return resized;
}
Recently, I realized that I can down-size with ImageMagick's 'convert' and then load the down-sized version that way. This saves additional 33%.
I was wondering, if there's an even better way.
EDIT: I realized that some people would wonder if my code is good in general, and the answer is NO. The code I used works well for me, because I down-size already small images (say 640x480, otherwise .getRGB() takes forever) and I don't care if a couple of color points spill over (carry-over from addition), and I know some people really care about that.
Here's a very good article on generating thumbnails in Java in an optimal way:
http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
You may have better results with specifying different scaling/rendering parameters.
Graphics2D g2 = (Graphics2D)g;
int newW = (int)(originalImage.getWidth() * xScaleFactor);
int newH = (int)(originalImage.getHeight() * yScaleFactor);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g2.drawImage(originalImage, 0, 0, newW, newH, null);