Rotate byte array of JPEG after onPictureTaken - android

Is there a way to rotate byte array without decoding it to Bitmap?
Currently in jpeg PictureCallback I just write byte array directly to file. But pictures are rotated. I would like to rotate them without decoding to bitmap with hope that this will conserve my memory.
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, o);
int orientation;
if (o.outHeight < o.outWidth) {
orientation = 90;
} else {
orientation = 0;
}
File photo = new File(tmp, "demo.jpeg");
FileOutputStream fos;
BufferedOutputStream bos = null;
try {
fos = new FileOutputStream(photo);
bos = new BufferedOutputStream(fos);
bos.write(data);
bos.flush();
} catch (IOException e) {
Log.e(TAG, "Failed to save photo", e);
} finally {
IOUtils.closeQuietly(bos);
}

Try this. It will solve the purpose.
Bitmap storedBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, null);
Matrix mat = new Matrix();
mat.postRotate("angle"); // angle is the desired angle you wish to rotate
storedBitmap = Bitmap.createBitmap(storedBitmap, 0, 0, storedBitmap.getWidth(), storedBitmap.getHeight(), mat, true);

You can set JPEG rotation via Exif header without decoding it. This is the most efficient method, but some viewers may still show a rotated image.
Alternatively, you can use JPEG lossless rotation. Unfortunately, I am not aware of free Java implementations of this algorithm.
Update on SourceForge, there is a Java open source class LLJTran. The Android port is on GitHub.

I don't think that there is such possibility. Bytes order depends from picture encoding (png, jpeg). So you are forced to decode image to do something with it.

Try like this,
private byte[] rotateImage(byte[] data, int angle) {
Log.d("labot_log_info","CameraActivity: Inside rotateImage");
Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length, null);
Matrix mat = new Matrix();
mat.postRotate(angle);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 100, stream);
return stream.toByteArray();
}
You can call the rotateImage by providing the image data which is getting from onPictureTaken method and an angle for rotation.
Eg: rotateImage(data, 90);

Related

How to rotate an image and save it without re compressing it?

I have an Image File (jpg) and i need to rotate it. However, i would like to avoid to re compress it when saving it back to the disk. Is their any way to do this?
I save the image like this:
matrix.setRotate(-90);
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
FileOutputStream fileoutputstream = new FileOutputStream(imagePath);
bmRotated.compress(CompressFormat.JPEG, 100, fileoutputstream);
fileoutputstream.flush();
fileoutputstream.close();
bmRotated.recycle();
Maybe you can try to extract the data from your bitmap to a byte array and save that array. (Code not tested, maybe it does not work).
Bitmap bitmap = new Bitmap()...
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int size = bitmap.getRowBytes() * bitmap.getHeight();
ByteBuffer byteBuffer = ByteBuffer.allocate(size);
bitmap.copyPixelsToBuffer(byteBuffer);
byte[] byteArray = byteBuffer.array();
FileOutputStream output = new FileOutputStream("filename");
output.write(byteArray);
output.close();
Use PNG instead of JPG. PNG format is loseless data format with compress.
https://en.wikipedia.org/wiki/Portable_Network_Graphics
matrix.setRotate(-90);
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
FileOutputStream fileoutputstream = new FileOutputStream(imagePath);
bmRotated.compress(CompressFormat.PNG, 100, fileoutputstream);
fileoutputstream.flush();
fileoutputstream.close();
bmRotated.recycle();
Or, you can use Compressor library, to use WEBP format.
WEBP supports both lossless and loss. https://en.wikipedia.org/wiki/WebP
https://github.com/zetbaitsu/Compressor
compressedImage = new Compressor(this)
.setMaxWidth(640)
.setMaxHeight(480)
.setQuality(100)
.setCompressFormat(Bitmap.CompressFormat.WEBP)
.setDestinationDirectoryPath(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath())
.compressToFile(actualImage);

Android opencv byte[] to mat to byte[]

My goal is to add an overlay on the camera preview that will find book edges. For that, I override the onPreviewFrame where I do the following:
public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
int width = parameters.getPreviewSize().width;
int height = parameters.getPreviewSize().height;
Mat mat = new Mat((int) (height*1.5), width, CvType.CV_8UC1);
mat.put(0,0,data);
byte[] bytes = new byte[(int) (height*width*1.5)];
mat.get(0,0,bytes);
if (!test) { //to only do once
File pictureFile = getOutputMediaFile();
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(bytes);
fos.close();
Uri picUri = Uri.fromFile(pictureFile);
updateGallery(picUri);
test = true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
For now I simply want to take one of the previews and save it after the conversion to mat.
After spending countless hours getting the above to look right, the saved picture cannot be seen on my testing phone (LG Leon). I can't seem to find the issue. Am I mixing the height/width because I'm taking pictures in portrait mode? I tried switching them and still doesn't work. Where is the problem?
The fastest method I managed to find is described HERE in my recently asked question. You can find the method to extract the image in the answer I wrote in my question below. The thing is that the image you get through onPreviewFrame() is NV21. After receiving this image it may be that you need to convert it to RGB (depends on what do you want to achieve; this is also done in the answer I gave you previously).
Seems quite inefficient but it works for me (for now):
//get the camera parameters
Camera.Parameters parameters = camera.getParameters();
int width = parameters.getPreviewSize().width;
int height = parameters.getPreviewSize().height;
//convert the byte[] to Bitmap through YuvImage;
//make sure the previewFormat is NV21 (I set it so somewhere before)
YuvImage yuv = new YuvImage(data, parameters.getPreviewFormat(), width, height, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, width, height), 70, out);
Bitmap bmp = BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.size());
//convert Bitmap to Mat; note the bitmap config ARGB_8888 conversion that
//allows you to use other image processing methods and still save at the end
Mat orig = new Mat();
bmp = bmp.copy(Bitmap.Config.ARGB_8888, true);
Utils.bitmapToMat(bmp, orig);
//here you do whatever you want with the Mat
//Mat to Bitmap to OutputStream to byte[] to File
Utils.matToBitmap(orig, bmp);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 70, stream);
byte[] bytes = stream.toByteArray();
File pictureFile = getOutputMediaFile();
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(bytes);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}

byte[] to image using Xamarin.Android

I know, this is an old question, but I've got problems with encoding a byte[] into a bitmap...
Background: I'm writing an Andoid-App which receives picturebytes via UDP, encodes them into a bitmap and displays the picture in an image view.
Since my functions didn't work, I cancelled the UDP-Connection for testing and wrote all the image-bytes in a huge variable. So they're all correct...
The function returns "null".
The function I'm using:
public Bitmap ByteArrayToImage(byte[] imageData)
{
var bmpOutput = BitmapFactory.DecodeByteArray(imageData, 0, imageData.Length);
return bmpOutput;
}
another function I tried out:
public Bitmap ByteArrayToImage2(byte[] imageData)
{
Bitmap bmpReturn;
bmpReturn = (Android.Graphics.Bitmap) Android.Graphics.Bitmap.FromArray<byte>(imageData);
return bmpReturn;
}
A function I found in the internet:
public static Bitmap bytesToUIImage (byte[] bytes)
{
if (bytes == null)
return null;
Bitmap bitmap;
var documentsFolder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
//Create a folder for the images if not exists
System.IO.Directory.CreateDirectory(System.IO.Path.Combine (documentsFolder, "images"));
string imatge = System.IO.Path.Combine (documents, "images", "image.jpg");
System.IO.File.WriteAllBytes(imatge, bytes.Concat(new Byte[]{(byte)0xD9}).ToArray());
bitmap = BitmapFactory.DecodeFile(imatge);
return bitmap;
}
Most unfortunately, the last function didn't work as well, but here I have do admit, that I was a bit confused about the 'documents' in
string imatge = System.IO.Path.Combine (documents, "images", "image.jpg");
I got an error and changed it into documentsFolder since i guess, that should (or could) be right....
Thank you in advance for your help
it seems, I found the error...
I stored the public Bitmap ByteArrayToImage(byte[] imageData) in another class. I don't know why, but when I decode the Bytearray in the class that also receives the array, all works fine...
If someone knows the reason, feel welcome to let me know, but for now I'm happy ;-)
I did something similar
On sender side:
Camera.Parameters parameters = camera.getParameters();
if (parameters.getPreviewFormat() == ImageFormat.NV21) {
Rect rect = new Rect(0, 0, parameters.getPreviewSize().width, parameters.getPreviewSize().height);
YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21, parameters.getPreviewSize().width, parameters.getPreviewSize().height, null);
ByteArrayOutputStream os = new ByteArrayOutputStream();
yuvimage.compressToJpeg(rect, 75, os);
byte[] videoFrame = os.toByteArray();
//send the video frame to reciever
}
On receiving side:
DataInputStream dIn = new DataInputStream(socket.getInputStream());
int length = 0;
length = dIn.readInt();
if (length > 0) {
byte[] message = new byte[length];
dIn.readFully(message, 0, message.length);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
final Bitmap bitmap = BitmapFactory.decodeByteArray(message, 0, message.length, options);
ReceiverActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
imgPreview.setImageBitmap(bitmap);
}
});
There is a built in method to decode a byte array into a bitmap. The problem comes when we are talking of big images. With small ones you can use:
Bitmap bmp = BitmapFactory.DecodeByteArray (data, 0, data.length);
Be aware. Those bitmaps are not mutable, so you will not be able to use canvases on those. To make them mutable go to: BitmapFactory.decodeResource returns a mutable Bitmap in Android 2.2 and an immutable Bitmap in Android 1.6

Out Of memory Error while sending the large bitmap to server

My question might be old but I am suffering because of this issue from last 2 weeks and now its too much, in my application I need to send large image bitmap to server and I am doing this by below coding:
BitmapFactory.Options bfOptions=new BitmapFactory.Options();
bfOptions.inDither=false; //Disable Dithering mode
bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
bfOptions.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
bfOptions.inTempStorage=new byte[32 * 1024];
File file=new File(TabGroupActivity.path);
FileInputStream input=null;
try {
input = new FileInputStream(TabGroupActivity.path);
} catch (FileNotFoundException e) {
//TODO do something intelligent
e.printStackTrace();
}
if(input!=null)
{
bitmap = BitmapFactory.decodeStream(input, null, bfOptions);
}
Matrix mat = new Matrix();//removing rotations
if(TabGroupActivity.rotation==90 || TabGroupActivity.rotation==270)
{
mat.setRotate(90);
}
else if(TabGroupActivity.rotation==0 || TabGroupActivity.rotation==180)
{
mat.setRotate(0);
}
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mat, true);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int height= bitmap.getHeight();
int width=bitmap.getWidth();
{
//formula for calculating aspect ratio
float k= (float) height/width;
newHeight =Math.round(620*k);
}
byte[] buffer=new byte[10];
while(input.read(buffer)!=-1)
{
bos.write(buffer);
}
bitmap = Bitmap.createScaledBitmap(bitmap , 620, newHeight, true);
bitmap.compress(CompressFormat.JPEG, 90, bos);
byte[] imageData = bos.toByteArray();
ContentBody cb = new ByteArrayBody(imageData, "image/jpg", "image1.jpeg");
input.close();
but after sending 2,3 images I am getting out of memory error on BitmapFactory.decodeStream() please help me the resolve this issue the main thing I can not re-size or crop image, I need to send good quality image only to server.
Recycle your bitmaps every time you are done with them by calling bitmap.recycle(), this should help a bit. Also optimize your code a bit; this part:
if(TabGroupActivity.rotation==90 || TabGroupActivity.rotation==270)
{
mat.setRotate(90);
}
else if(TabGroupActivity.rotation==0 || TabGroupActivity.rotation==180)
{
mat.setRotate(0);
}
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mat, true);
You can reduce to
if(TabGroupActivity.rotation==90 || TabGroupActivity.rotation==270)
{
mat.setRotate(90);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mat, true);
}
So you don't have to create a new bitmap in every case.
Also, you can experiment with the BitmapFactory.Options.inSampleSize to get smaller images.

Android Rotate Picture before saving

I just finished my camera activity and it's wonderfully saving the data.
What I do after the picture is taken:
protected void savePictureData() {
try {
FileOutputStream fs = new FileOutputStream(this.photo);
fs.write(this.lastCamData);
fs.close(); //okay, wonderful! file is just written to the sdcard
//---------------------
//---------------------
//TODO in here: dont save just the file but ROTATE the image and then save it!
//---------------------
//---------------------
Intent data = new Intent(); //just a simple intent returning some data...
data.putExtra("picture_name", this.fname);
data.putExtra("byte_data", this.lastCamData);
this.setResult(SAVED_TOOK_PICTURE, data);
this.finish();
} catch (IOException e) {
e.printStackTrace();
this.IOError();
}
}
What I want to is already as comment given in the code above. I dont want the image just to be saved to file but to be rotated and then saved! Thanks!
//EDIT: What I am currently up to (Works but still runs into memory issues with large images)
byte[] pictureBytes;
Bitmap thePicture = BitmapFactory.decodeByteArray(this.lastCamData, 0, this.lastCamData.length);
Matrix m = new Matrix();
m.postRotate(90);
thePicture = Bitmap.createBitmap(thePicture, 0, 0, thePicture.getWidth(), thePicture.getHeight(), m, true);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
thePicture.compress(CompressFormat.JPEG, 100, bos);
pictureBytes = bos.toByteArray();
FileOutputStream fs = new FileOutputStream(this.photo);
fs.write(pictureBytes);
fs.close();
Intent data = new Intent();
data.putExtra("picture_name", this.fname);
data.putExtra("byte_data", pictureBytes);
this.setResult(SAVED_TOOK_PICTURE, data);
this.finish();
Read the path from sd card and paste the following code...It'll Replace the existing photo after rotating it..
Note: Exif doesn't work on most of the devices, it returns incorrect data so it's good to hard code the rotation before saving to any degree you want to, Just change the angle value in postRotate.
String photopath = tempphoto.getPath().toString();
Bitmap bmp = BitmapFactory.decodeFile(photopath);
Matrix matrix = new Matrix();
matrix.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
FileOutputStream fOut;
try {
fOut = new FileOutputStream(tempphoto);
bmp.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
fOut.flush();
fOut.close();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Before you create your FileOutputStream you can create a new Bitmap from the original that has been transformed using a Matrix. To do that you would use this method:
createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
Where the m defines a matrix that will transpose your original bitmap.
For an example on how to do this look at this question:
Android: How to rotate a bitmap on a center point
bitmap = RotateBitmap(bitmap, 90);
public static Bitmap RotateBitmap(Bitmap source, float angle)
{
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}

Categories

Resources