I’m developing an android application for face recognition, using JavaCV which is an unofficial wrapper of OpenCV. After importing com.googlecode.javacv.cpp.opencv_contrib.FaceRecognizer,
I apply and test the following known methods:
LBPH using createLBPHFaceRecognizer() method
FisherFace using createFisherFaceRecognizer() method
EigenFace using createEigenFaceRecognizer() method
Before I recognize the detected face, I correct the rotated face and crop the proper zone, inspiring from this method
In general when I pass on camera a face already exist in the database, the recognition is ok. But this is not always correct. Sometimes it recognizes the unknown face (not found in Database of trained samples) with a high probability. When we have in the DB two or more faces of similar features (beard, mustache, glasses...) the recognition may be highly mistaken between those faces!
To predict the result using the test face image, I apply the following code:
public String predict(Mat m) {
int n[] = new int[1];
double p[] = new double[1];
IplImage ipl = MatToIplImage(m,WIDTH, HEIGHT);
faceRecognizer.predict(ipl, n, p);
if (n[0]!=-1)
mProb=(int)p[0];
else
mProb=-1;
if (n[0] != -1)
return labelsFile.get(n[0]);
else
return "Unkown";
}
I can’t control the threshold of the probability p, because:
Small p < 50 could predict a correct result.
High p > 70 could predict a false result.
Middle p could predict a correct or false.
As well, I don’t understand why predict() function gives sometime a probability greater than 100 in case of using LBPH??? and in case of Fisher and Eigen it gives very big values (>2000) ??
Can someone help in finding a solution for these bizarre problems?
Is there any suggestion to improve robustness of recognition? especially in case of similarity of two different faces.
The following is the entire class using Facerecognizer:
package org.opencv.javacv.facerecognition;
import static com.googlecode.javacv.cpp.opencv_highgui.*;
import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;
import static com.googlecode.javacv.cpp.opencv_contrib.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.util.ArrayList;
import org.opencv.android.Utils;
import org.opencv.core.Mat;
import com.googlecode.javacv.cpp.opencv_imgproc;
import com.googlecode.javacv.cpp.opencv_contrib.FaceRecognizer;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
import com.googlecode.javacv.cpp.opencv_core.MatVector;
import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
public class PersonRecognizer {
public final static int MAXIMG = 100;
FaceRecognizer faceRecognizer;
String mPath;
int count=0;
labels labelsFile;
static final int WIDTH= 128;
static final int HEIGHT= 128;;
private int mProb=999;
PersonRecognizer(String path)
{
faceRecognizer = com.googlecode.javacv.cpp.opencv_contrib.createLBPHFaceRecognizer(2,8,8,8,200);
// path=Environment.getExternalStorageDirectory()+"/facerecog/faces/";
mPath=path;
labelsFile= new labels(mPath);
}
void changeRecognizer(int nRec)
{
switch(nRec) {
case 0: faceRecognizer = com.googlecode.javacv.cpp.opencv_contrib.createLBPHFaceRecognizer(1,8,8,8,100);
break;
case 1: faceRecognizer = com.googlecode.javacv.cpp.opencv_contrib.createFisherFaceRecognizer();
break;
case 2: faceRecognizer = com.googlecode.javacv.cpp.opencv_contrib.createEigenFaceRecognizer();
break;
}
train();
}
void add(Mat m, String description) {
Bitmap bmp= Bitmap.createBitmap(m.width(), m.height(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(m,bmp);
bmp= Bitmap.createScaledBitmap(bmp, WIDTH, HEIGHT, false);
FileOutputStream f;
try {
f = new FileOutputStream(mPath+description+"-"+count+".jpg",true);
count++;
bmp.compress(Bitmap.CompressFormat.JPEG, 100, f);
f.close();
} catch (Exception e) {
Log.e("error",e.getCause()+" "+e.getMessage());
e.printStackTrace();
}
}
public boolean train() {
File root = new File(mPath);
Log.i("mPath",mPath);
FilenameFilter pngFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".jpg");
};
};
File[] imageFiles = root.listFiles(pngFilter);
MatVector images = new MatVector(imageFiles.length);
int[] labels = new int[imageFiles.length];
int counter = 0;
int label;
IplImage img=null;
IplImage grayImg;
int i1=mPath.length();
for (File image : imageFiles) {
String p = image.getAbsolutePath();
img = cvLoadImage(p);
if (img==null)
Log.e("Error","Error cVLoadImage");
Log.i("image",p);
int i2=p.lastIndexOf("-");
int i3=p.lastIndexOf(".");
int icount=Integer.parseInt(p.substring(i2+1,i3));
if (count<icount) count++;
String description=p.substring(i1,i2);
if (labelsFile.get(description)<0)
labelsFile.add(description, labelsFile.max()+1);
label = labelsFile.get(description);
grayImg = IplImage.create(img.width(), img.height(), IPL_DEPTH_8U, 1);
cvCvtColor(img, grayImg, CV_BGR2GRAY);
images.put(counter, grayImg);
labels[counter] = label;
counter++;
}
if (counter>0)
if (labelsFile.max()>1)
faceRecognizer.train(images, labels);
labelsFile.Save();
return true;
}
public boolean canPredict()
{
if (labelsFile.max()>1)
return true;
else
return false;
}
public String predict(Mat m) {
if (!canPredict())
return "";
int n[] = new int[1];
double p[] = new double[1];
IplImage ipl = MatToIplImage(m,WIDTH, HEIGHT);
// IplImage ipl = MatToIplImage(m,-1, -1);
faceRecognizer.predict(ipl, n, p);
if (n[0]!=-1)
mProb=(int)p[0];
else
mProb=-1;
// if ((n[0] != -1)&&(p[0]<95))
if (n[0] != -1)
return labelsFile.get(n[0]);
else
return "Unkown";
}
IplImage MatToIplImage(Mat m,int width,int heigth)
{
Bitmap bmp=Bitmap.createBitmap(m.width(), m.height(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(m, bmp);
return BitmapToIplImage(bmp,width, heigth);
}
IplImage BitmapToIplImage(Bitmap bmp, int width, int height) {
if ((width != -1) || (height != -1)) {
Bitmap bmp2 = Bitmap.createScaledBitmap(bmp, width, height, false);
bmp = bmp2;
}
IplImage image = IplImage.create(bmp.getWidth(), bmp.getHeight(),
IPL_DEPTH_8U, 4);
bmp.copyPixelsToBuffer(image.getByteBuffer());
IplImage grayImg = IplImage.create(image.width(), image.height(),
IPL_DEPTH_8U, 1);
cvCvtColor(image, grayImg, opencv_imgproc.CV_BGR2GRAY);
return grayImg;
}
protected void SaveBmp(Bitmap bmp,String path)
{
FileOutputStream file;
try {
file = new FileOutputStream(path , true);
bmp.compress(Bitmap.CompressFormat.JPEG,100,file);
file.close();
}
catch (Exception e) {
// TODO Auto-generated catch block
Log.e("",e.getMessage()+e.getCause());
e.printStackTrace();
}
}
public void load() {
train();
}
public int getProb() {
// TODO Auto-generated method stub
return mProb;
}
}
I think you need to implement something to be more robust to illumination changes. see: Illumination normalization in OpenCV
Then, in order to manage similarity between images maybe you can use something like Principal component Analysis.
Related
I come up against the great problem.
I`m try to BebopVideoView to Mat.
(BebopVideoView is parrot drone source code)
But I was failed for several days.
Here is the code.
package com.hyeonjung.dronecontroll.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.parrot.arsdk.arcontroller.ARCONTROLLER_STREAM_CODEC_TYPE_ENUM;
import com.parrot.arsdk.arcontroller.ARControllerCodec;
import com.parrot.arsdk.arcontroller.ARFrame;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BebopVideoView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "BebopVideoView";
private static final String VIDEO_MIME_TYPE = "video/avc";
private static final int VIDEO_DEQUEUE_TIMEOUT = 33000;
private MediaCodec mMediaCodec;
private Lock mReadyLock;
private boolean mIsCodecConfigured = false;
private ByteBuffer mSpsBuffer;
private ByteBuffer mPpsBuffer;
private ByteBuffer[] mBuffers;
private static final int VIDEO_WIDTH = 640;
private static final int VIDEO_HEIGHT = 368;
public byte[] a;
public Mat k;
public BebopVideoView(Context context) {
super(context);
customInit();
}
public BebopVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
customInit();
}
public BebopVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
customInit();
}
private void customInit() {
mReadyLock = new ReentrantLock();
getHolder().addCallback(this);
}
public void displayFrame(ARFrame frame) {
mReadyLock.lock();
if ((mMediaCodec != null)) {
if (mIsCodecConfigured) {
// Here we have either a good PFrame, or an IFrame
int index = -1;
try {
index = mMediaCodec.dequeueInputBuffer(VIDEO_DEQUEUE_TIMEOUT);
} catch (IllegalStateException e) {
Log.e(TAG, "Error while dequeue input buffer");
}
if (index >= 0) {
ByteBuffer b;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
b = mMediaCodec.getInputBuffer(index); // fill inputBuffer with valid data
}
else {
b = mBuffers[index]; // fill inputBuffer with valid data
b.clear();
}
if (b != null) {
b.put(frame.getByteData(), 0, frame.getDataSize()); //write to b.
getMat(frame);
saveMat(k);
}
try {
mMediaCodec.queueInputBuffer(index, 0, frame.getDataSize(), 0, 0); //end of stream
} catch (IllegalStateException e) {
Log.e(TAG, "Error while queue input buffer");
}
}
}
// Try to display previous frame
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int outIndex;
try {
outIndex = mMediaCodec.dequeueOutputBuffer(info, 0);
while (outIndex >= 0) {
mMediaCodec.releaseOutputBuffer(outIndex, true);
outIndex = mMediaCodec.dequeueOutputBuffer(info, 0);
}
} catch (IllegalStateException e) {
Log.e(TAG, "Error while dequeue input buffer (outIndex)");
}
}
mReadyLock.unlock();
}
public void configureDecoder(ARControllerCodec codec) {
mReadyLock.lock();
if (codec.getType() == ARCONTROLLER_STREAM_CODEC_TYPE_ENUM.ARCONTROLLER_STREAM_CODEC_TYPE_H264) {
ARControllerCodec.H264 codecH264 = codec.getAsH264();
mSpsBuffer = ByteBuffer.wrap(codecH264.getSps().getByteData());
mPpsBuffer = ByteBuffer.wrap(codecH264.getPps().getByteData());
}
if ((mMediaCodec != null) && (mSpsBuffer != null)) {
configureMediaCodec();
}
mReadyLock.unlock();
}
private void configureMediaCodec() {
MediaFormat format = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, VIDEO_WIDTH, VIDEO_HEIGHT);
format.setByteBuffer("csd-0", mSpsBuffer);
format.setByteBuffer("csd-1", mPpsBuffer);
mMediaCodec.configure(format, getHolder().getSurface(), null, 0);
mMediaCodec.start();
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
mBuffers = mMediaCodec.getInputBuffers();
}
mIsCodecConfigured = true;
}
private void initMediaCodec(String type) {
try {
mMediaCodec = MediaCodec.createDecoderByType(type);
} catch (IOException e) {
Log.e(TAG, "Exception", e);
}
if ((mMediaCodec != null) && (mSpsBuffer != null)) {
configureMediaCodec();
}
}
private void releaseMediaCodec() {
if (mMediaCodec != null) {
if (mIsCodecConfigured) {
mMediaCodec.stop();
mMediaCodec.release();
}
mIsCodecConfigured = false;
mMediaCodec = null;
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mReadyLock.lock();
initMediaCodec(VIDEO_MIME_TYPE);
mReadyLock.unlock();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mReadyLock.lock();
releaseMediaCodec();
mReadyLock.unlock();
}
public void getMat(ARFrame frame) {
k = new Mat();
k.get(150, 150, frame.getByteData());
k.put(150, 150, frame.getByteData());
//or
//byte[] a= new byte[b.remaining()];
//b.get(a);
//k.get(150, 150, a);
//k.put(150, 150, a);
}
public void saveMat (Mat mat) {
Mat mIntermediateMat = new Mat(150, 150, CvType.CV_8UC1);
Imgproc.cvtColor(mat, mIntermediateMat, Imgproc.COLOR_GRAY2BGR);
File path = new File(Environment.getExternalStorageDirectory() + "/data");
path.mkdirs();
File file = new File(path, "image.png");
String filename = file.toString();
Boolean bool = Imgcodecs.imwrite(filename, mIntermediateMat);
if (bool)
Log.i(TAG, "SUCCESS writing image to external storage");
else
Log.i(TAG, "Fail writing image to external storage");
}
}
I think I can get an image related data from ByteBuffer b or frame.get ByteData ().
I was confirmed ByteBuffer b and frame.getByteData().
There was char data type with a range of -128 to 127.
So I was confirmed the result of getMat, saveMat and the result was a NULL(Mat k).
What is wrong?
Please help me T.T
If you use a TextureView you can simply grab a bitmap of it and convert it to a Mat. You need to use the TextureView's provided surface rather than the typical SurfaceView holder. This will require some additional refactoring of the mediaCodec lifecycle, but is a fairly trivial change.
public class BebopVideoView extends TextureView implements TextureView.SurfaceTextureListener {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
this.surface = new Surface(surface);
surfaceCreated = true;
}
...
}
And inside of configureMediaCodec use the class level surface captured in onSurfaceTextureAvailable instead....
mediaCodec.configure(format, surface, null, 0);
With a couple other minor tweaks you now have a lot more control over the view. You can do things like setTransform() and more importantly in your case getBitmap:
Mat mat = new Mat();
Utils.bitmapToMat(getBitmap(), mat);
i'm currently working on a LWJGL-program that displays an .obj model with it's textures and materials(render materials not added yet, but doesn't matter for this question). Loading the files and store theire data is no problem - in the end i have a WaveFrontObject.class (As posted below) that contains all the required information about my model including a List of it's single parts/groups. Also rendering them using glVertex3f(),glTextureCoord2f() and glNormal3f() works great. But since some of the models get pretty big(8MB or more) i want to make my rendering algorythm more efficient using VAOs and VBOs.
My problem is, that i wanna render one, all or a custom set of modelparts/groupobjects and apply a custom translation/rotation&scale to them.
To keep it clean and simple i want to use only one VAO per entity. So i somehow need to add/remove some parts of my vertices-VBO & indicesVBO or at least somehow decide which parts of my model should actually be rendered.
What is the best way of doing this?
This is what i have so far - Sorry if there are some unnecessary code parts, i tried out many things but it didn't work so i decided to come here and ask:
WaveFrontObject.class (The Main OBJLoader&OBJModel.class):
package objmodels;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import Toolbox.FileUtils;
import program.RLMEditor;
import shaders.StaticShader;
import textures.ModelTexture;
public class WaveFrontObject {
private String fileName;
public List<Vector3f> vertices = new ArrayList<Vector3f>();
public List<Vector3f> normals = new ArrayList<Vector3f>();
public List<Vector2f> textureCoordinates = new ArrayList<Vector2f>();
public List<GroupObject> groupObjects = new ArrayList<GroupObject>();
private GroupObject currentGroupObject = null;
public int textureID;
public boolean loadedTexture = false;
public File texture;
public int numLines;
public RLMEditor program;
public WaveFrontObject(File objFile,RLMEditor parent, boolean transferProgress) {
this.textureID = ModelTexture.getStandardModel().getTextureID();
this.fileName = objFile.getPath();
this.program = parent;
if (isValidOBJFile(objFile)) {
loadOBJModel(objFile, transferProgress);
}
}
public WaveFrontObject(File objFile) {
this.textureID = ModelTexture.getStandardModel().getTextureID();
this.fileName = objFile.getPath();
if (isValidOBJFile(objFile)) {
loadOBJModel(objFile, false);
}
}
private void loadOBJModel(File objFile, boolean transferProgress) {
if (transferProgress)
try {
//program.loadingProgress = new FrameProgressBar(numLines);
//program.loadingProgress.setVisible(true);
numLines = FileUtils.countLines(objFile.getPath());
} catch (IOException e1) {
e1.printStackTrace();
}
try {
FileReader fileReader;
fileReader = new FileReader(objFile);
BufferedReader reader = new BufferedReader(fileReader);
String line = null;
int lineCount = 0;
while ((line = reader.readLine()) != null) {
lineCount++;
if (transferProgress) {
// program.loadingProgress.addStep(1);
}
if (line.startsWith("#") || line.isEmpty()) {
} else if (line.startsWith("v ")) {
System.out.println(line);
Vector3f vertex = parseVertex(line.split(" "));
vertices.add(vertex);
} else if (line.startsWith("vn ")) {
Vector3f normal = parseNormal(line.split(" "));
normals.add(normal);
} else if (line.startsWith("vt ")) {
Vector2f texture = parseTexture(line.split(" "));
textureCoordinates.add(texture);
} else if (line.startsWith("f ")) {
if (currentGroupObject == null) {
currentGroupObject = new GroupObject("default");
}
Face face = parseFace(line, lineCount);
if (face != null) {
currentGroupObject.faces.add(face);
}
} else if (line.startsWith("o ") || line.startsWith("g ")) {
GroupObject group = parseGroupObject(line, lineCount);
if (group != null) {
if (currentGroupObject != null) {
groupObjects.add(currentGroupObject);
}
}
currentGroupObject = group;
currentGroupObject.validate();
if(canAddGroupObject(currentGroupObject)){
groupObjects.add(currentGroupObject);
}else
System.out.println("The GroupObject '"+currentGroupObject.name+"' was already loaded!");
}
}
//Parsing textureCoords to shader
reader.close();
} catch (FileNotFoundException e) {
System.err.println("[FILENAME ERROR] The following File could not be found: " + fileName + "!");
e.printStackTrace();
} catch (IOException e) {
System.err.println("[IO ERROR] FileReader for file: " + fileName + " could not be created!");
e.printStackTrace();
}
}
private boolean canAddGroupObject(GroupObject currentGroupObject) {
for(GroupObject groupObject:groupObjects){
if(groupObject.name.equalsIgnoreCase(currentGroupObject.name)){
return false;
}
}
return true;
}
public void renderAll(StaticShader shader) {
if (currentGroupObject != null) {
//GL11.glBegin(currentGroupObject.drawingMode);
} else {
//GL11.glBegin(GL11.GL_TRIANGLES);
}
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
renderAllObjects(shader);
//GL11.glEnd();
}
private void renderAllObjects(StaticShader shader) {
for (GroupObject object : groupObjects) {
object.render(shader);
}
}
public void renderOnly(String... groupNames) {
for (GroupObject groupObject : groupObjects) {
for (String groupName : groupNames) {
if (groupName.equalsIgnoreCase(groupObject.name)) {
renderObject(groupObject, program.window.shader);
//groupObject.render(program.window.shader);
}
}
}
}
private void renderObject(GroupObject groupObject, StaticShader shader) {
// Bind to the VAO that has all the information about the vertices
GL30.glBindVertexArray(groupObject.vaoID);
GL20.glEnableVertexAttribArray(0);
// Bind to the index VBO that has all the information about the order of the vertices
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, groupObject.vboiID);
// Draw the vertices
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, groupObject.getNumVertices());
// Put everything back to default (deselect)
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
public void renderPart(String partName) {
for (GroupObject groupObject : groupObjects) {
if (partName.equalsIgnoreCase(groupObject.name)) {
System.out.println("Rendering " + groupObject.name);
groupObject.render(program.window.shader);
}
}
}
private GroupObject parseGroupObject(String line, int lineCount) {
GroupObject group = null;
if (isValidGroupObjectLine(line)) {
String trimmedLine = line.substring(line.indexOf(" ") + 1);
if (trimmedLine.length() > 0) {
group = new GroupObject(trimmedLine);
}
} else {
System.err.println("[OBJ FORMAT ERROR]");
System.out.print("Parser was unable to read line number " + lineCount + "!");
}
return group;
}
private boolean isValidGroupObjectLine(String line) {
return line.split(" ").length == 2;
}
private Face parseFace(String line, int lineCount) {
Face face = null;
String[] faceVertices = line.split(" ");
String[] subTokens = null;
if (faceVertices.length == 4) {
currentGroupObject.drawingMode = GL11.GL_TRIANGLES;
face = new Face();
face.vertices = new Vector3f[faceVertices.length - 1];
face.vertexNormals = new Vector3f[faceVertices.length - 1];
face.textureCoords = new Vector2f[faceVertices.length - 1];
for (int i = 1; i < faceVertices.length; i++) {
subTokens = faceVertices[i].split("/");
int currentVertexPointer = Integer.parseInt(subTokens[0]);
face.indices.add(currentVertexPointer);
face.vertices[i - 1] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
face.textureCoords[i - 1] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1);
face.vertexNormals[i - 1] = normals.get(Integer.parseInt(subTokens[2]) - 1);
}
face.faceNormal = face.calculateFaceNormal();
}
if (faceVertices.length == 5) {
currentGroupObject.drawingMode = GL11.GL_QUADS;
System.err.println("[OBJ FORMAT ERROR]");
System.out.print("The .obj parser can currently only load models with triangulated faces!");
}
return face;
}
private Vector2f parseTexture(String[] textureData) {
Vector2f texture = new Vector2f(Float.parseFloat(textureData[1]), Float.parseFloat(textureData[2]));
return texture;
}
private Vector3f parseNormal(String[] normalData) {
Vector3f normal = new Vector3f(Float.parseFloat(normalData[1]), Float.parseFloat(normalData[2]),
Float.parseFloat(normalData[3]));
return normal;
}
private Vector3f parseVertex(String[] vertexData) {
Vector3f vertex = new Vector3f(Float.parseFloat(vertexData[1]), Float.parseFloat(vertexData[2]),
Float.parseFloat(vertexData[3]));
return vertex;
}
private boolean isValidVertex(String[] vertexData) {
return vertexData.length == 4;
}
private boolean isValidNormal(String[] normalData) {
return normalData.length == 4;
}
private boolean isValidTexture(String[] textureData) {
return textureData.length == 4;
}
private boolean isValidOBJFile(File objfile) {
return objfile.getName().endsWith(".obj") || objfile.getName().endsWith(".OBJ");
}
public File getFileLocation() {
return new File(fileName);
}
public void bindTexture(File textureFile) {
Texture texture = null;
try {
texture = TextureLoader.getTexture("png", new FileInputStream(textureFile));
this.textureID = texture.getTextureID();
this.texture = textureFile;
} catch (Exception e) {
e.printStackTrace();
}
}
public float[] getVertexArray(){
float[] verticesArray = new float[vertices.size()*3];
for(int i = 0; i<vertices.size(); i++){
verticesArray[i*3]=vertices.get(i).x;
verticesArray[i*3+1]=vertices.get(i).y;
verticesArray[i*3+2]=vertices.get(i).z;
}
return verticesArray;
}
public float[] getTextureArray(){
float[] texturesArray = new float[textureCoordinates.size()*2];
for(int i = 0; i<textureCoordinates.size(); i++){
texturesArray[i*2]=textureCoordinates.get(i).x;
texturesArray[i*2+1]=textureCoordinates.get(i).y;
}
return texturesArray;
}
public void cleanUp(){
for(GroupObject groupObject:groupObjects){
groupObject.cleanUp();
}
}
}
GroupObject.class - Containing all faces from a single modelpart, it's name and the glDrawingMode (currently this can only be GL11.GL_TRIANGLES):
package objmodels;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Vector3f;
import shaders.StaticShader;
public class GroupObject {
public int vaoID;
public int vboID;
public int vboiID;
private boolean isValid; //Used to set the vertex-&indicesArray only once
public String name;
public List<Face> faces = new ArrayList<Face>();
public int drawingMode;
private FloatBuffer verticesBuffer;
private IntBuffer indicesBuffer;
private int numIndices;
public GroupObject(String name) {
this.name = name;
}
public void prepareRendering(){
vaoID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoID);
// Create a new VBO for the indices and select it (bind)
vboID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
// Put the VBO in the attributes list at index 0
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
// Deselect (bind to 0) the VBO
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// Deselect (bind to 0) the VAO
GL30.glBindVertexArray(0);
vboiID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiID);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
// Deselect (bind to 0) the VBO
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
}
public void render(StaticShader shader) {
// Bind to the VAO that has all the information about the vertices
GL30.glBindVertexArray(vaoID);
GL20.glEnableVertexAttribArray(0);
// Bind to the index VBO that has all the information about the order of the vertices
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiID);
// Draw the vertices
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, getNumVertices());
// Put everything back to default (deselect)
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
public void cleanUp(){
// Disable the VBO index from the VAO attributes list
GL20.glDisableVertexAttribArray(0);
// Delete the vertex VBO
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vboID);
// Delete the index VBO
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vboiID);
// Delete the VAO
GL30.glBindVertexArray(0);
GL30.glDeleteVertexArrays(vaoID);
}
public void renderFaces(StaticShader shader) {
if (faces.size() > 0) {
for (Face face : faces) {
face.addFaceForRender(shader);
}
}
}
public void validate() {
this.isValid=true;
this.verticesBuffer = getVertexBuffer();
this.indicesBuffer = getIndicesBuffer();
}
public FloatBuffer getVertexBuffer() {
float[] verticesArray = new float[getNumVertices()];
for (int facePointer = 0; facePointer < faces.size(); facePointer++) {
Face face = faces.get(facePointer);
for (Vector3f faceVertex : face.vertices) {
verticesArray[facePointer] = faceVertex.x;
verticesArray[facePointer + 1] = faceVertex.y;
verticesArray[facePointer + 2] = faceVertex.z;
}
}
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(verticesArray.length);
verticesBuffer.put(verticesArray);
verticesBuffer.flip();
return verticesBuffer;
}
public IntBuffer getIndicesBuffer() {
List<Integer> indices = new ArrayList<Integer>();
for (int facePointer = 0; facePointer < faces.size(); facePointer++) {
Face face = faces.get(facePointer);
int[] indicesArray = face.getIndicesArray();
for (int i = 0; i < indicesArray.length; i++) {
indices.add(indicesArray[i]);
numIndices++;
}
}
int[] indicesArray = new int[indices.size()];
for (int index = 0; index < indices.size(); index++) {
indicesArray[index] = indices.get(index);
}
IntBuffer indicesBuffer = BufferUtils.createIntBuffer(indicesArray.length);
indicesBuffer.put(indicesArray);
indicesBuffer.flip();
return indicesBuffer;
}
public int getNumVertices() {
int number = 0;
for (Face face : faces) {
number += face.getNumVertices();
}
return number;
}
}
Face.class-Holding the information about a single Face(Vertices,Normals&Texturecoords):
package objmodels;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import shaders.StaticShader;
public class Face {
public Vector3f[] vertices;
public Vector3f[] vertexNormals;
public Vector2f[] textureCoords;
public Vector3f faceNormal;
public List<Integer> indices = new ArrayList<Integer>();
public Face() {
this.vertices = new Vector3f[3];
this.vertexNormals = new Vector3f[3];
this.textureCoords = new Vector2f[3];
}
public Face(Vector3f[] vertices, Vector3f[] vertexNormals, Vector2f[] textureCoords) {
this.vertices = vertices;
this.vertexNormals = vertexNormals;
this.textureCoords = textureCoords;
this.faceNormal = calculateFaceNormal();
}
public void setVertex(int index, Vector3f vertex, Vector3f normal, Vector2f textureCoords) {
this.vertices[index] = vertex;
this.vertexNormals[index] = normal;
this.textureCoords[index] = textureCoords;
}
public Vector3f calculateFaceNormal() {
Vector3f v1 = new Vector3f(vertices[1].x - vertices[0].x, vertices[1].y - vertices[0].y,
vertices[1].z - vertices[0].z);
Vector3f v2 = new Vector3f(vertices[2].x - vertices[0].x, vertices[2].y - vertices[0].y,
vertices[2].z - vertices[0].z);
Vector3f normalVector = null;
normalVector = (Vector3f) Vector3f.cross(v1, v2, normalVector).normalise();
return new Vector3f((float) normalVector.x, (float) normalVector.y, (float) normalVector.z);
}
public void addFaceForRender(StaticShader shader) {
Vector3f normal = calculateFaceNormal();
for (int i = 0; i < vertices.length; i++) {
{
// int textureCoordsLocation =
// GL20.glGetAttribLocation(shader.programID, "textureCoords");
Vector3f vertex = vertices[i];
Vector2f textureCoord = textureCoords[i];
// GL20.glVertexAttrib2f(textureCoordsLocation, textureCoord.x,
// textureCoord.y);
GL11.glTexCoord2f(textureCoord.x, textureCoord.y);
GL11.glVertex3d(vertex.x, vertex.y, vertex.z);
GL11.glNormal3d(normal.x, normal.y, normal.z);
}
}
}
public int getNumVertices() {
return vertices.length;
}
public int[] getIndicesArray() {
int[] indicesArray = new int[indices.size()];
for (int i = 0; i < indices.size(); i++) {
indicesArray[i]=indices.get(i);
}
return indicesArray;
}
}
Thanks in advice - ItsAMysterious
Unfortunately, you posted too much code (~570 lines, wow!) so I can't read it all. But you are solving a very common problem, and I can give you the general solution.
The problem is, "I want to draw different models using one shared VAO." This is actually a lot easier than it sounds. You can simply concatenate all of your models into the same VBOs, and then when you call glDrawElements() or glDrawArrays(), you specify which part of the array you want to use (using glDrawElementsBaseVertex() instead of glDrawElements(), if necessary).
For example, let's say we have models A, B, and C. Let's say that model A has 1000 vertexes, model B has 500, and model C has 750.
First, concatenate all the vertex data into one VBO, and use one VAO for everything. Now you can call:
glDrawArrays(GL_TRIANGLES, 0, 1000); // model A
glDrawArrays(GL_TRIANGLES, 1000, 500); // model B
glDrawArrays(GL_TRIANGLES, 1500, 750); // model C
If you are using glDrawElements(), you have two choices.
You can pass ranges to glDrawElements(), and have the index array point into the combined VAO, or
You can pass ranges to glDrawElementsBaseVertex(), and have the index array be relative to the first vertex in each model.
Either way, it's common a common technique to reduce the number of state changes. Another alternative is to use glBindVertexBuffer(), specifying a different offset into your VBO each time, but this is only in core as of 4.3 and it's a bit more flexible than you need.
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import common.ResourcesToAccess;
public class RecordingStartingThread extends Thread{
#Override
public void run(){
JFrame f = new JFrame();
ImageIcon reel = new ImageIcon("src/images/reel.GIF");
JLabel label = new JLabel(reel);
reel.setImageObserver(label);
f.getContentPane().add(label);
f.setUndecorated(true);
f.setSize(300, 300);
f.setVisible(true);
}
public static void main(String[] args) {
new RecordingStartingThread().start();
}
}
Issue: GIF plays extremely fast.
Question: How do I make sure that GIF plays at a normal speed?
As for GIF speed playback - I've encountered this problem too. If I remember correctly this was caused by the "default" (or not provided?) value for frame rate in GIF file. Some web browsers "overrided" that frame rate so that GIF played correctly.
As a result I created a class that converts GIF (read GIF -> write GIF) and give frame rate provided by the user. com.madgag.gif.fmsware.AnimatedGifEncoder class is an external library that I link to the project via Maven: animated-gif-lib-1.0.jar
public final class GIFUtils {
private GIFUtils() {
}
public static List<BufferedImage> extractFrames(String filePath) throws IOException {
return extractFrames(new File(filePath));
}
public static List<BufferedImage> extractFrames(File file) throws IOException {
List<BufferedImage> imgs = new LinkedList<BufferedImage>();
ImageReader reader = ImageIO.getImageReadersBySuffix("GIF").next();
ImageInputStream in = null;
try {
in = ImageIO.createImageInputStream(new FileInputStream(file));
reader.setInput(in);
BufferedImage img = null;
int count = reader.getNumImages(true);
for(int i = 0; i < count; i++) {
Node tree = reader.getImageMetadata(i).getAsTree("javax_imageio_gif_image_1.0");
int x = Integer.valueOf(tree.getChildNodes().item(0).getAttributes()
.getNamedItem("imageLeftPosition").getNodeValue());
int y = Integer.valueOf(tree.getChildNodes().item(0).getAttributes()
.getNamedItem("imageTopPosition").getNodeValue());
BufferedImage image = reader.read(i);
if(img == null) {
img = new BufferedImage(image.getWidth() + x, image.getHeight() + y,
BufferedImage.TYPE_4BYTE_ABGR);
}
Graphics2D g = img.createGraphics();
ImageUtils.setBestRenderHints(g);
g.drawImage(image, x, y, null);
imgs.add(ImageUtils.copy(img));
}
}
finally {
if(in != null) {
in.close();
}
}
return imgs;
}
public static void writeGif(List<BufferedImage> images, File gifFile, int millisForFrame)
throws FileNotFoundException, IOException {
BufferedImage firstImage = images.get(0);
int type = firstImage.getType();
ImageOutputStream output = new FileImageOutputStream(gifFile);
// create a gif sequence with the type of the first image, 1 second
// between frames, which loops continuously
GifSequenceWriter writer = new GifSequenceWriter(output, type, 100, false);
// write out the first image to our sequence...
writer.writeToSequence(firstImage);
for(int i = 1; i < images.size(); i++) {
BufferedImage nextImage = images.get(i);
writer.writeToSequence(nextImage);
}
writer.close();
output.close();
}
public static Image createGif(List<BufferedImage> images, int millisForFrame) {
AnimatedGifEncoder g = new AnimatedGifEncoder();
ByteArrayOutputStream out = new ByteArrayOutputStream(5 * 1024 * 1024);
g.start(out);
g.setDelay(millisForFrame);
g.setRepeat(1);
for(BufferedImage i : images) {
g.addFrame(i);
}
g.finish();
byte[] bytes = out.toByteArray();
return Toolkit.getDefaultToolkit().createImage(bytes);
}
And GifSequenceWriter looks like this:
public class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
public GifSequenceWriter(ImageOutputStream outputStream, int imageType, int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType);
imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("delayTime", Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int loop = loopContinuously ? 0 : 1;
child.setUserObject(new byte[] { 0x1, (byte) (loop & 0xFF), (byte) (loop >> 8 & 0xFF) });
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
gifWriter.setOutput(outputStream);
gifWriter.prepareWriteSequence(null);
}
public void writeToSequence(RenderedImage img) throws IOException {
gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
}
public void close() throws IOException {
gifWriter.endWriteSequence();
}
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if(!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
}
return iter.next();
}
private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) {
int nNodes = rootNode.getLength();
for(int i = 0; i < nNodes; i++) {
if(rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) {
return (IIOMetadataNode) rootNode.item(i);
}
}
IIOMetadataNode node = new IIOMetadataNode(nodeName);
rootNode.appendChild(node);
return node;
}
}
The easiest fix for this problem is to just create a .gif file which is "well-formed", i.e. contains the appropriate frame rate. This can be achieved with the help of various online converters, just google "gif speed changer".
I am having trouble printing to a label printer. The code below prints 4 "labels" on one (Picture of Label Attached).
The code below prints to a brother QL-500 label printer. It prints onto 3.5" by 1.1" labels.
It would also be great if someone could help me better understand the code.
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import javax.print.PrintService;
public class DYMOLabelPrintConnector implements Printable {
public static final String PRINTERNAME = "DYMO LabelWriter 400";
public static final boolean PRINTMENU = false;
public static void main(String[] args) {
PrinterJob printerJob = PrinterJob.getPrinterJob();
PageFormat pageFormat = printerJob.defaultPage();
Paper paper = new Paper();
final double widthPaper = (1.2 * 72);
final double heightPaper = (1.5 * 72);
paper.setSize(widthPaper, heightPaper);
paper.setImageableArea(0, 0, widthPaper, heightPaper);
pageFormat.setPaper(paper);
pageFormat.setOrientation(PageFormat.LANDSCAPE);
if (PRINTMENU) {
if (printerJob.printDialog()) {
printerJob.setPrintable(new DYMOLabelPrintConnector(), pageFormat);
try {
printerJob.print();
} catch (PrinterException e) {
e.printStackTrace();
}
}
} else {
PrintService[] printService = PrinterJob.lookupPrintServices();
for (int i = 0; i < printService.length; i++) {
System.out.println(printService[i].getName());
if (printService[i].getName().compareTo(PRINTERNAME) == 0) {
try {
printerJob.setPrintService(printService[i]);
printerJob.setPrintable(new DYMOLabelPrintConnector(), pageFormat);
printerJob.print();
} catch (PrinterException e) {
e.printStackTrace();
}
}
}
}
System.exit(0);
}
public String getValue(final int elementOnLabel, final int labelCounter) {
String value = "";
switch (elementOnLabel) {
case 0:
// what ever you want to have in this line
value = "SetupX";
break;
case 1:
// what ever you want to have in this line
value = "fiehnlab.ucd";
break;
case 2:
// what ever you want to have in this line
value = "id: " + labelCounter;
break;
case 3:
// what ever you want to have in this line
// TODO - add DB connection
value = "label:" + elementOnLabel;
break;
case 4:
// what ever you want to have in this line
value = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US).format(new Date());
break;
default:
break;
}
return value;
}
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
System.out.println("printing page: " + pageIndex);
if (pageIndex < getPageNumbers()) {
Graphics2D g = (Graphics2D) graphics;
// g.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
g.translate(20, 10);
String value = "";
pageIndex = pageIndex + 1;
// specific for four circular labels per page
for (int x = 0; x < 80; x = x + 50) {
for (int y = 0; y < 80; y = y + 36) {
int posOnPage = 4; // BottomRight, TopRight, BottomLeft, TopLeft
if (x > 0) {
posOnPage = posOnPage - 2;
}
if (y > 0) {
posOnPage = posOnPage - 1;
}
// current counter for the label.
int id = (posOnPage - 1) + ((pageIndex - 1) * 4);
// setupx
g.setFont(new Font(g.getFont().getFontName(), g.getFont().getStyle(), 3));
value = this.getValue(0, id);
g.drawString(" " + value, x, y);
// fiehnlab
g.setFont(new Font(g.getFont().getFontName(), g.getFont().getStyle(), 3));
value = this.getValue(1, id);
g.drawString(" " + value, x, y + 4);
// ID
g.setFont(new Font(g.getFont().getFontName(), Font.BOLD, 7));
value = this.getValue(2, id);
g.drawString("" + value, x, y + 10);
// label
g.setFont(new Font(g.getFont().getFontName(), g.getFont().getStyle(), 5));
value = this.getValue(3, id);
g.drawString(" " + value, x, y + 16);
// date
g.setFont(new Font(g.getFont().getFontName(), Font.PLAIN, 3));
value = this.getValue(4, id);
g.drawString(" " + value, x, y + 20);
}
}
return PAGE_EXISTS;
} else {
return NO_SUCH_PAGE;
}
}
public int getPageNumbers() {
return 5;
}
}
enter code here
Here is What it Prints:
Wow, I can't tell you how much I love printing in Java, when it works, it's great...
.
public class PrinterTest {
public static void main(String[] args) {
PrinterJob pj = PrinterJob.getPrinterJob();
if (pj.printDialog()) {
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
double width = fromCMToPPI(3.5);
double height = fromCMToPPI(8.8);
paper.setSize(width, height);
paper.setImageableArea(
fromCMToPPI(0.25),
fromCMToPPI(0.5),
width - fromCMToPPI(0.35),
height - fromCMToPPI(1));
System.out.println("Before- " + dump(paper));
pf.setOrientation(PageFormat.PORTRAIT);
pf.setPaper(paper);
System.out.println("After- " + dump(paper));
System.out.println("After- " + dump(pf));
dump(pf);
PageFormat validatePage = pj.validatePage(pf);
System.out.println("Valid- " + dump(validatePage));
//Book book = new Book();
//book.append(new MyPrintable(), pf);
//pj.setPageable(book);
pj.setPrintable(new MyPrintable(), pf);
try {
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
}
protected static double fromCMToPPI(double cm) {
return toPPI(cm * 0.393700787);
}
protected static double toPPI(double inch) {
return inch * 72d;
}
protected static String dump(Paper paper) {
StringBuilder sb = new StringBuilder(64);
sb.append(paper.getWidth()).append("x").append(paper.getHeight())
.append("/").append(paper.getImageableX()).append("x").
append(paper.getImageableY()).append(" - ").append(paper
.getImageableWidth()).append("x").append(paper.getImageableHeight());
return sb.toString();
}
protected static String dump(PageFormat pf) {
Paper paper = pf.getPaper();
return dump(paper);
}
public static class MyPrintable implements Printable {
#Override
public int print(Graphics graphics, PageFormat pageFormat,
int pageIndex) throws PrinterException {
System.out.println(pageIndex);
int result = NO_SUCH_PAGE;
if (pageIndex < 2) {
Graphics2D g2d = (Graphics2D) graphics;
System.out.println("[Print] " + dump(pageFormat));
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
g2d.draw(new Rectangle2D.Double(1, 1, width - 1, height - 1));
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString("0x0", 0, fm.getAscent());
result = PAGE_EXISTS;
}
return result;
}
}
}
I'm aware of a number of inconsistencies with the PrintDialog doing werid and wonderful things if you try and specify Paper sizes and margins, but honestly, that's a question for another day.
The code I've posted was capable for printing two labels one after the other without issue on my Dymo LabelWriter 400 Turbo
UPDATED
Should also mention, I think you were basically missing PageFormat.setPaper
UPDATED with Barbaque BarCode
Print from file example...
Barcode b = BarcodeFactory.createCode128("Hello");
b.setResolution(72);
File f = new File("mybarcode.png");
// Let the barcode image handler do the hard work
BarcodeImageHandler.savePNG(b, f);
.
.
.
public static class PrintFromFile implements Printable {
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
if (pageIndex == 0) {
graphics.translate((int)pageFormat.getImageableX(), (int)pageFormat.getImageableY());
result = PAGE_EXISTS;
try {
// You may want to rescale the image to better fit the label??
BufferedImage read = ImageIO.read(new File("mybarcode.png"));
graphics.drawImage(read, 0, 0, null);
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}
Printing direct to the Graphics context
Barcode b = BarcodeFactory.createCode128("Hello");
b.setResolution(72);
.
.
.
public static class PrintToGraphics implements Printable {
private Barcode b;
private PrintToGraphics(Barcode b) {
this.b = b;
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
if (pageIndex == 0) {
result = PAGE_EXISTS;
int x = (int)pageFormat.getImageableX();
int y = (int)pageFormat.getImageableY();
int width = (int)pageFormat.getImageableWidth();
int height = (int)pageFormat.getImageableHeight();
graphics.translate(x, y);
try {
b.draw((Graphics2D)graphics, 0, 0);
} catch (OutputException ex) {
ex.printStackTrace();
}
}
return result;
}
}
Last but not least, directly from the "examples" directory of the download
public class PrintingExample
{
public static void main(String[] args)
{
try
{
Barcode b = BarcodeFactory.createCode128("Hello");
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(b);
if (job.printDialog())
{
job.print();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
#After a long hour of analysis found 100% solution
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STPageOrientation;
public class CreateWord {
public static void main(String[] args) throws FileNotFoundException, IOException {
XWPFDocument doc = new XWPFDocument();
CTDocument1 document = doc.getDocument();
CTBody body = document.getBody();
if (!body.isSetSectPr()) {
body.addNewSectPr();
}
CTSectPr section = body.getSectPr();
if (!section.isSetPgSz()) {
section.addNewPgSz();
}
if (!section.isSetPgMar()) {
CTPageMar margin = section.addNewPgMar();
margin.setTop(BigInteger.valueOf(500));
margin.setBottom(BigInteger.valueOf(100));
margin.setLeft(BigInteger.valueOf(0));
margin.setRight(BigInteger.valueOf(0));
}
CTPageSz pageSize = section.getPgSz();
pageSize.setOrient(STPageOrientation.LANDSCAPE);
pageSize.setW(BigInteger.valueOf(6000));
pageSize.setH(BigInteger.valueOf(2000));
XWPFParagraph title = doc.createParagraph();
XWPFRun run = title.createRun();
// run.setText("Fig.1 A Natural Scene");
// run.setBold(true);
// title.setAlignment(ParagraphAlignment.CENTER);
String imgFile = "E:\\barcode.png";
FileInputStream is = new FileInputStream(imgFile);
// run.addBreak();
try {
run.addPicture(is, XWPFDocument.PICTURE_TYPE_JPEG, imgFile, Units.toEMU(300), Units.toEMU(70));
} catch (InvalidFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 200x200
// pixels
is.close();
FileOutputStream fos = new FileOutputStream("E:\\test4.docx");
doc.write(fos);
fos.close();
}
}
[1]: https://i.stack.imgur.com/VA0IL.png
[2]: https://i.stack.imgur.com/9aSv4.png
I have a similar code, but can't make the Toshiba portable printer print in TPCL mode, my code works fine but only when the printer is in ESC/POS mode. My printer is Toshiba B-EP4-DL
This is my MainActivity:
package com.example.warehousev3;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.mazenrashed.printooth.Printooth;
import com.mazenrashed.printooth.data.printable.Printable;
import com.mazenrashed.printooth.data.printable.RawPrintable;
import com.mazenrashed.printooth.data.printable.TextPrintable;
import com.mazenrashed.printooth.data.printer.DefaultPrinter;
import com.mazenrashed.printooth.ui.ScanningActivity;
import com.mazenrashed.printooth.utilities.Printing;
import com.mazenrashed.printooth.utilities.PrintingCallback;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
public class MainActivity extends AppCompatActivity implements PrintingCallback {
DatabaseHelper myDb;
Button btnPrint, btn_unpair_pair;
EditText barcodefield = null;
Printing printing;
private Calendar calendar;
private SimpleDateFormat dateFormat;
private String date;
private final static char ESC_CHAR = 0x1B; // to be used on esc/pos commands
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
calendar = Calendar.getInstance();
dateFormat = new SimpleDateFormat("dd/MM/yyyy");
date = dateFormat.format(calendar.getTime());
myDb = new DatabaseHelper(this);
btnPrint = findViewById(R.id.cmd_print);
barcodefield = findViewById(R.id.txt_barcode);
btn_unpair_pair = findViewById(R.id.btnPiarUnpair);
btnPrint.setVisibility(View.GONE); //Set Print Button Invisible
viewAll();
}
public void viewAll() {
if (printing != null)
printing.setPrintingCallback(this);
btn_unpair_pair.setOnClickListener(view -> {
if (Printooth.INSTANCE.hasPairedPrinter())
Printooth.INSTANCE.removeCurrentPrinter();
else {
startActivityForResult(new Intent(MainActivity.this, ScanningActivity.class), ScanningActivity.SCANNING_FOR_PRINTER);
changePairAndUpair();
}
});
/* //Print Button function
btnPrint.setOnClickListener(view -> {
if (!Printooth.INSTANCE.hasPairedPrinter())
startActivityForResult(new Intent(MainActivity.this,
ScanningActivity.class), ScanningActivity.SCANNING_FOR_PRINTER);
else
printText();
barcodefield.setText("");
});
*/
barcodefield.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
for (int i = 0; i < s.length(); i++) {
if(barcodefield.getText().toString().length() == 13) {
if (!Printooth.INSTANCE.hasPairedPrinter())
startActivityForResult(new Intent(MainActivity.this, ScanningActivity.class), ScanningActivity.SCANNING_FOR_PRINTER);
else
printText();
barcodefield.setText("");
}
}
}
});
changePairAndUpair();
}
private void changePairAndUpair() {
if (Printooth.INSTANCE.hasPairedPrinter())
btn_unpair_pair.setText(new StringBuilder("Unpair
").append(Printooth.INSTANCE.getPairedPrinter().getName()).toString());
else
btn_unpair_pair.setText("Pair with Printer");
}
#Override
public void connectingWithPrinter() {
Toast.makeText(this, "Connectiong to printer", Toast.LENGTH_SHORT).show();
}
#Override
public void connectionFailed(String s) {
Toast.makeText(this, "Failed: "+s, Toast.LENGTH_SHORT).show();
}
#Override
public void onError(String s) {
Toast.makeText(this, "Error: "+s, Toast.LENGTH_SHORT).show();
}
#Override
public void onMessage(String s) {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
#Override
public void printingOrderSentSuccessfully() {
Toast.makeText(this, "Order sent to printer", Toast.LENGTH_SHORT).show();
}
private void printText() {
ArrayList<Printable> printables = new ArrayList<>();
printables.add(new RawPrintable.Builder(new byte[]{27, 100, 4}).build());
//Original Code
//printables.add(new RawPrintable.Builder(new byte[]{0x1B,'d', 5}).build()); //
FEED 5 LINES WORKING!!!!!!!!
//printables.add(new RawPrintable.Builder(new byte[]{'L','H', 0x56}).build()); //
FEED 10 LINES WORKING!!!!!!!!
//printables.add(new RawPrintable.Builder(new byte[]{}).build()); // lane for
test
myDb.setBarcodcheck(String.valueOf(barcodefield.getText()));
Cursor res = myDb.getAllData();
if (res.getCount() == 0) {
showMessage("Error", "Nothing Found");
return;
}
StringBuffer buffer = new StringBuffer();
while (res.moveToNext()) {
buffer.append(res.getString(0)+"\n");
buffer.append(res.getString(1)+"\n");
buffer.append(res.getString(2)+"\n");
buffer.append(date+"\n");
buffer.append("\n");
}
printables.add(new TextPrintable.Builder()
.setText(buffer.toString()) // Original code
//.setText(String.valueOf(buffer))
.setCharacterCode(DefaultPrinter.Companion.getCHARCODE_PC1252())
.setLineSpacing(DefaultPrinter.Companion.getLINE_SPACING_60())
.setEmphasizedMode(DefaultPrinter.Companion.getEMPHASIZED_MODE_BOLD())
.setFontSize(DefaultPrinter.Companion.getFONT_SIZE_LARGE())
//.setNewLinesAfter(1)
.build());
//printables.add(new RawPrintable.Builder(new byte[]{0x1B,'d', 5}).build()); // FEED 5 LINES WORKING!!!!!!!!
//printables.add(new RawPrintable.Builder(new byte[]{}).build()); // Line for test
Printooth.INSTANCE.printer().print(printables);
}
public void showMessage(String title, String Message) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setCancelable(true);
builder.setTitle(title);
builder.setMessage(Message);
builder.show();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent
data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ScanningActivity.SCANNING_FOR_PRINTER && resultCode ==
Activity.RESULT_OK)
initPrinting();
changePairAndUpair();
}
private void initPrinting() {
if (!Printooth.INSTANCE.hasPairedPrinter())
printing = Printooth.INSTANCE.printer();
if (printing != null)
printing.setPrintingCallback(this);
}
}
I am developing an application where i need to save the thumbnails of the image in a folder inside the folder where the images are . The image folder is selected by means of a file chooser.
I am having a problem saving the thumbnails. It says an error message a file not found exception rather.
The code that i have written is :
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ThumbnailFactory {
public ThumbnailFactory() {
}
public void run(String folder) {
savepath = folder+"\\thumbnails";
File dir = new File(folder);
for (File file : dir.listFiles()) {
createThumbnail(file);
}
}
private void createThumbnail(File file) {
try {
// BufferedImage is the best (Toolkit images are less flexible)
BufferedImage img = ImageIO.read(file);
BufferedImage thumb = createEmptyThumbnail();
// BufferedImage has a Graphics2D
Graphics2D g2d = (Graphics2D) thumb.getGraphics();
g2d.drawImage(img, 0, 0,
thumb.getWidth() - 1,
thumb.getHeight() - 1,
0, 0,
img.getWidth() - 1,
img.getHeight() - 1,
null);
g2d.dispose();
ImageIO.write(thumb, "PNG", createOutputFile(file));
} catch (Exception e) {
e.printStackTrace();
}
}
private File createOutputFile(File inputFile) throws IOException {
System.out.println(savepath+"\\"+inputFile.getName());
File f = new File(savepath+"\\"+inputFile.getName()+".png");
if(!f.exists())
{
System.out.println("Creating the file in thumbnail directory");
f.createNewFile();
}
return new File(savepath+"\\"+inputFile.getName()+".png") ;
}
private BufferedImage createEmptyThumbnail() {
return new BufferedImage(100, 200,
BufferedImage.TYPE_INT_RGB);
}
private String savepath;
}
It throws a FileNotFoundException , NullPointerException in the createOutputFile() method, at the f.createNewFile() point .
The input file is the image in the folder selected . I have to place a thumbnail of this image inside a folder created inside the folder selected.
For example,
Selected image folder is D:\pictures
Then i need to place a thumbnail of every picture inside D:\pictures in D:\pictures\thumbnails.
please point out the mistake that i am doing and how to correct it .
Rather than writing all of that code yourself, you might look into using the Thumbnailator library. Your entire example can be written in the following few lines, which expresses your intent much better.
public class ThumbnailFactory {
public void run(String folder) {
Thumbnails.of(new File(folder+"\\thumbnails").listFiles())
.size(100,200)
.outputFormat("png")
.asFiles(Rename.SUFFIX_HYPTHEN_THUMBNAIL);
}
}
I wanted to play with a final static THUMBNAIL, and G2D... It didn't work, but this does if you can live with the ill-proportioned results of making all images the same size, and therefore shape, regardless of there original dimensions ;-)
package forums;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
public class ThumbnailFactory
{
private static final String THUMBNAILS_SUBDIR_NAME = File.separator + "thumbnails";
private final File _thumbsSubdir;
private final File _picsDir;
public ThumbnailFactory(String picsDirectoryPath) {
_picsDir = new File(picsDirectoryPath);
_thumbsSubdir = new File(thumbDirectoryPath(_picsDir));
}
private static String thumbDirectoryPath(File picsDir) {
return picsDir.getAbsolutePath()+THUMBNAILS_SUBDIR_NAME;
}
public void createThumbnails() throws IOException {
if (!_thumbsSubdir.exists()) {
_thumbsSubdir.mkdir();
}
for (File picFile : _picsDir.listFiles(
new FilenameFilter() {
public boolean accept(File f, String s) {
return s.toLowerCase().endsWith(".jpg");
}
}
)) {
if ( !createThumbnail(picFile, new File(thumbFilename(picFile))) )
break;
}
}
private String thumbFilename(File pictureFile) {
return _thumbsSubdir.getAbsolutePath()
+ File.separator
+ pictureFile.getName()
+ ".png";
}
private boolean createThumbnail(File pictureFile, File thumbFile)
throws IOException
{
boolean retval = false;
BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) image.getGraphics();
BufferedImage picture = ImageIO.read(pictureFile);
if (picture!=null) {
g2d.drawImage(
picture
, 0, 0, image.getWidth()-1, image.getHeight()-1
, 0, 0, picture.getWidth()-1, picture.getHeight()-1
, null
);
retval = ImageIO.write(image, "PNG", thumbFile);
System.out.println(thumbFile);
}
return retval;
}
public static void main(String... args) {
try {
ThumbnailFactory factory = new ThumbnailFactory("C:/Users/Administrator/Pictures");
factory.createThumbnails();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'm glad you got it sorted out ;-)
Cheers. Keith.