Bitmap is cropped wrong in onPictureTaken - android

I want to implement my own face detection/recognition android app. When camera finds some face, a rectangle is displayed on camera preview (in real time). App has method for taking photos too. However, I dont want to save whole picture, only the area within the rectangle - the human face. When I give the rectangle coordinates to Bitmap.createBitmap method to crop my picture, correctness of cropped photo depends on the place on display, where the rectangle was shown. When a detected face appears in the middle of preview, createBitmap crops it circa fine, but not if it shows on left or right side of the display. Seems like the coordinates I send to Bitmap.createBitmap are conversed but I cannot find the ratio. Any solutions?
Here is my onPictureTaken method:
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile();
if (pictureFile == null) {
Log.d(TAG, "Error creating media file, check storage permissions: ");
return;
}
Bitmap picture = BitmapFactory.decodeByteArray(data, 0, data.length);
RectF faceRect = mPreview.getFaceRect();
float x = faceRect.left;
float y = faceRect.top;
float w = faceRect.right - faceRect.left;
float h = faceRect.bottom - faceRect.top;
int intX = (int) x;
int intY = (int) y;
int intW = (int) w;
int intH = (int) h;
Bitmap croppedPicture = Bitmap.createBitmap(picture, intX, intY, intW, intH);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
croppedPicture.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArrayFromPicture = stream.toByteArray();
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(byteArrayFromPicture);
//fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
and here is some example of cropped picture, I do not have enough reputation to post more links:
face close to the left edge of display
cropped pic1
(sorry about making picture of picture, I was lazy to implement saving the rectangle together with photo)

Resolved
The solution was very simple - because of using frontal camera the captured image was always reflected, added two if-clauses:
Bitmap picture = BitmapFactory.decodeByteArray(data, 0, data.length);
RectF faceRect = mPreview.getFaceRect();
Camera.Parameters parameters = mCamera.getParameters();
int picWidth = parameters.getPictureSize().width;
int intX = 0;
int intY = (int) faceRect.top;
int intW = (int) (faceRect.right - faceRect.left);
int intH = (int) (faceRect.bottom - faceRect.top);
if(faceRect.left > picWidth / 2) {
intX = (int) (faceRect.right - (faceRect.right - picWidth / 2) * 2);
}
else if(faceRect.left <= picWidth / 2) {
intX = (int) (picWidth - faceRect.right);
}
Bitmap croppedPicture = Bitmap.createBitmap(picture, intX, intY, intW, intH);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
croppedPicture.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArrayFromPicture = stream.toByteArray();

Related

Face Detection API- coordinates

So I am using the API to detect faces in images, and it is working well for me so far. I have not been able to figure out how to crop the image to the face however. I know how to crop the Bitmap, but it requires getting the top left position of the face in the Bitmap and width and height. When I query for the top left position using
points = face.getPosition();
Bitmap bmp = Bitmap.createBitmap(bit,(int)points.x,(int)(-1.0*points.y),(int)face.getWidth(),(int)face.getHeight());
But when I look at points, I notice that y is -63.5555 and x is 235.6666; I dont understand why there is a negative y coordinate. I did some Debugging and looked inside the face object; I found that it contained a PointF object already that had positive x and y coordinates. So why is a negative y coordinate being returned in this case?
The bounding box estimates the dimensions of the head, even though it may not be entirely visible within the photo. The coordinates may be negative if the face is cropped by the top or left of the image (e.g., the top of the head is cropped off the top of the picture, resulting in a y coordinate above 0).
The difference that you see in debugging is due to that fact that the implementation internally uses the head center position to represent the position (approximately at the mid-point between the eyes), but the API translates this to the top-left position when you call getPosition, for your convenience.
Also note that the bounding box is not necessarily a tight bounds on the face. If you want a tighter fit, you should enable landmark detection and compute your desired level of cropping relative to the returned landmarks.
I have used the same API before and was able to successfully crop the face.
Try
//Crop face option
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
//Bitmap bitmap = BitmapFactory.decodeFile(pictureFile.getAbsolutePath(), options);
Bitmap bitmap = getRotatedImageToUpload(pictureFile.getAbsolutePath());
Bitmap faceBitmap = Bitmap.createBitmap(bitmap, (int) faceCentre.x, (int) faceCentre.y, (int) faceWidth, (int) faceHeight);
FileOutputStream out = null;
try {
out = new FileOutputStream(getOutputMediaFile());
faceBitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance
// PNG is a lossless format, the compression factor (100) is ignored
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//End of Crop face option
And the code for getRotateImageToUpload is
public Bitmap getRotatedImageToUpload(String filePath) {
try {
String file = filePath;
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, bounds);
BitmapFactory.Options opts = new BitmapFactory.Options();
Bitmap bm = BitmapFactory.decodeFile(file, opts);
ExifInterface exif = null;
exif = new ExifInterface(file);
String orientString = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
int orientation = orientString != null ? Integer.parseInt(orientString) : ExifInterface.ORIENTATION_NORMAL;
int rotationAngle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) rotationAngle = 90;
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) rotationAngle = 180;
if (orientation == ExifInterface.ORIENTATION_ROTATE_270) rotationAngle = 270;
Matrix matrix = new Matrix();
matrix.setRotate(rotationAngle, (float) bm.getWidth() / 2, (float) bm.getHeight() / 2);
Bitmap rotatedBitmap = Bitmap.createBitmap(bm, 0, 0, bounds.outWidth, bounds.outHeight, matrix, true);
return rotatedBitmap;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

4 channel IplImage javacv to android bitmap

I'm trying to record video by checking each frame of camera preview to bitmap with quality ARGB_8888. As it required 4 channel, Created IplImage with channel 4 too. Now the output have two major problems :
1) Bitmap that created from IplImage have grayscale. even if I have converted it from BGR2RGBA.
2) 4 channel IplImage gave me bitmap (divided in 4 parts) with same screen.
Let me put my code over here.
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (yuvIplimage != null && recording) {
videoTimestamp = 1000 * (System.currentTimeMillis() - startTime);
// Where imagewidth = 640 and imageheight = 480 (As per camera preview size)
// Create the yuvIplimage
IplImage yuvimage = IplImage.create(imageWidth, imageHeight * 3 / 2, IPL_DEPTH_8U, 2);
yuvimage.getByteBuffer().put(data);
IplImage rgbimage = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 3);
opencv_imgproc.cvCvtColor(yuvimage, rgbimage, opencv_imgproc.CV_YUV2BGR_NV21);
Bitmap bitmap = Bitmap.createBitmap(imageWidth, imageHeight,Bitmap.Config.RGB_565);
bitmap.copyPixelsFromBuffer(rgbimage.getByteBuffer());
//Save file to SDCARD------------
File file = new File(Environment.getExternalStorageDirectory(),
"rgbbitmap.png");
FileOutputStream fOut;
try {
fOut = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
// mybitmap.recycle();
} catch (Exception e) { // TODO
}
try {
// Get the correct time
recorder.setTimestamp(videoTimestamp);
// Record the image into FFmpegFrameRecorder
recorder.record(yuvimage);
} catch (FFmpegFrameRecorder.Exception e) {
Log.v(LOG_TAG, e.getMessage());
e.printStackTrace();
}
}
}
As well as, find the below bitmap image as I'm getting as output with 4 parts of same frame.
What's wrong with my code or what's missing by me? Let me know your best suggestions.
Thanks,
IplImage yuvImage = IplImage.create(width, height * 3 / 2, IPL_DEPTH_8U, 1);
yuvImage.getByteBuffer().put(data);
IplImage bgrImage = IplImage.create(width, height, IPL_DEPTH_8U, 3);
cvCvtColor(yuvImage, bgrImage, CV_YUV2BGR_NV21);
cvSaveImage("/mnt/sdcard/result.jpg", bgrImage);

Android take photo and resize it before saving on sd card

I want my code to resize the image before saving but I can't find anything about it on Google.
Could you help me please ?
This is the code (from Android doc) :
private void galleryAddPic() {
Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
File f = new File(mCurrentPhotoPath);
picturePathForUpload = mCurrentPhotoPath;
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
After that, I have to upload it to a server.
Thanks a lot.
You can save Bitmap image following code
Bitmap photo = (Bitmap) "your Bitmap image";
photo = Bitmap.createScaledBitmap(photo, 100, 100, false);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.JPEG, 40, bytes);
File f = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Imagename.jpg");
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.close();
After reading the other answers and not finding exactly what I wanted, here's my approach at acquiring an appropriately scaled bitmap. This is an adaptation of Prabu's answer.
It makes sure your picture is scaled in a way that does not deform the dimensions of the photo:
public saveScaledPhotoToFile() {
//Convert your photo to a bitmap
Bitmap photoBm = (Bitmap) "your Bitmap image";
//get its orginal dimensions
int bmOriginalWidth = photoBm.getWidth();
int bmOriginalHeight = photoBm.getHeight();
double originalWidthToHeightRatio = 1.0 * bmOriginalWidth / bmOriginalHeight;
double originalHeightToWidthRatio = 1.0 * bmOriginalHeight / bmOriginalWidth;
//choose a maximum height
int maxHeight = 1024;
//choose a max width
int maxWidth = 1024;
//call the method to get the scaled bitmap
photoBm = getScaledBitmap(photoBm, bmOriginalWidth, bmOriginalHeight,
originalWidthToHeightRatio, originalHeightToWidthRatio,
maxHeight, maxWidth);
/**********THE REST OF THIS IS FROM Prabu's answer*******/
//create a byte array output stream to hold the photo's bytes
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
//compress the photo's bytes into the byte array output stream
photoBm.compress(Bitmap.CompressFormat.JPEG, 40, bytes);
//construct a File object to save the scaled file to
File f = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Imagename.jpg");
//create the file
f.createNewFile();
//create an FileOutputStream on the created file
FileOutputStream fo = new FileOutputStream(f);
//write the photo's bytes to the file
fo.write(bytes.toByteArray());
//finish by closing the FileOutputStream
fo.close();
}
private static Bitmap getScaledBitmap(Bitmap bm, int bmOriginalWidth, int bmOriginalHeight, double originalWidthToHeightRatio, double originalHeightToWidthRatio, int maxHeight, int maxWidth) {
if(bmOriginalWidth > maxWidth || bmOriginalHeight > maxHeight) {
Log.v(TAG, format("RESIZING bitmap FROM %sx%s ", bmOriginalWidth, bmOriginalHeight));
if(bmOriginalWidth > bmOriginalHeight) {
bm = scaleDeminsFromWidth(bm, maxWidth, bmOriginalHeight, originalHeightToWidthRatio);
} else {
bm = scaleDeminsFromHeight(bm, maxHeight, bmOriginalHeight, originalWidthToHeightRatio);
}
Log.v(TAG, format("RESIZED bitmap TO %sx%s ", bm.getWidth(), bm.getHeight()));
}
return bm;
}
private static Bitmap scaleDeminsFromHeight(Bitmap bm, int maxHeight, int bmOriginalHeight, double originalWidthToHeightRatio) {
int newHeight = (int) Math.min(maxHeight, bmOriginalHeight * .55);
int newWidth = (int) (newHeight * originalWidthToHeightRatio);
bm = Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
return bm;
}
private static Bitmap scaleDeminsFromWidth(Bitmap bm, int maxWidth, int bmOriginalWidth, double originalHeightToWidthRatio) {
//scale the width
int newWidth = (int) Math.min(maxWidth, bmOriginalWidth * .75);
int newHeight = (int) (newWidth * originalHeightToWidthRatio);
bm = Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
return bm;
}
Here's a corresponding link to my GitHub Gist: https://gist.github.com/Lwdthe1/2d1cd0a12f30c18db698
First convert your image to bitmap then use this code:
Bitmap yourBitmap;
Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);
See this it will be help full. In general if your are taking an image by camera using intent you will get the uri of image as result you read it as scale down image and store it in the same place
If you want to capture a full size image, then you have no choice but to save it to the sd cart and then change the size of the image.
If however thumbnail image is sufficient then there is no need to save it to SD card and you can extract it from the extras of the returned intent.
You can look at this guide I wrote for both methods of taking images using the build in camera Activity:
Guide: Android: Use Camera Activity for Thumbnail and Full Size Image
BitmapFactory.Options optionsSignature = new BitmapFactory.Options();
final Bitmap bitmapSignature = BitmapFactory.decodeFile(
fileUriSignature.getPath(), optionsSignature);
Bitmap resizedSignature = Bitmap.createScaledBitmap(
bitmapSignature, 256, 128, true);
signature.setImageBitmap(resizedSignature);

image rotation not working on Samsung Galaxy Nexus android

I've tested this code snippet on about 25 devices and it works great on all of them except a Samsung Galaxy Nexus that I'm trying to test with now.
Here is the method and I apologize for not trimming it down to find the exact spot that's throwing the exception, but eclipse's debugging is doodoo.
private void setupImageView() {
imageLocation = currentPhotoPath;
// Get the dimensions of the View
Display display = getWindowManager().getDefaultDisplay();
Point size = getDisplaySize(display);
int targetW = size.x;
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imageLocation, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW / targetW, photoH / targetW);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(imageLocation, bmOptions);
//int rotationForImage = getRotationForImage(imageLocation);
int rotationForImage = (whichCamera == 0 ? 90 : 270);
if (rotationForImage != 0) {
int targetWidth = rotationForImage == 90 || rotationForImage == 270 ? bitmap.getHeight() : bitmap.getWidth();
int targetHeight = rotationForImage == 90 || rotationForImage == 270 ? bitmap.getWidth() : bitmap.getHeight();
Bitmap rotatedBitmap = Bitmap.createBitmap(targetWidth, targetHeight, bitmap.getConfig());
Canvas canvas = new Canvas(rotatedBitmap);
Matrix matrix = new Matrix();
matrix.setRotate(rotationForImage, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
canvas.drawBitmap(bitmap, matrix, new Paint());
bitmap.recycle();
bitmap = rotatedBitmap;
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 40, bytes);
try
{
File f = new File(imageLocation);
f.createNewFile();
//write the bytes in file
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.close();
}
catch(java.io.IOException e){}
}
imageView.setImageBitmap(bitmap);
}
anyone know what Samsung does differently with the nexus that would cause this to throw an exception? It works fine on a Galaxy S III
It looks like something in the if block you mention is throwing an NPE - that's the real bug here. Don't worry about the Activity/ResultInfo stuff, that is downstream and triggered by the NPE. Go line by line and look for the null reference :-)
Regarding Eclipse - sadly I don't have much experience there. For Android I personally use IntelliJ and the debugging works well. Are you able to debug other Java code (even a simple Hello, World)?

Android Canvas drawBitmap function efficiency

I'm trying to display a bitmap on canvas using the matrix.
canvas.drawBitmap(currentBitmap, m_matrix, tempPaint);
but the result appears, image seems weird. After that I have shown it using bounds
canvas.drawBitmap(currentBitmap, 0, 0, tempPaint);
But here image looks good, in both the cases image is not scaled.
How should I set matrix properties for the initial display?
Does the matrix display uses something else for showing the image because of that image is getting changed?
Please suggest any tutorials for details explanation.
After searching lot a lot i have finally found the solution, some of the threads related to same issue helped me out.
Quality Issue
Quality problems when resizing an image at runtime
I have followed the google's Tutorials for loading the bitmap and written my own function for scaling the bitmap.
public static Bitmap scaleDownBitmap(Bitmap original, boolean recycleOriginal, int newmaxWidth , int newmaxHeight){
if(original == null)
return null;
Bitmap rtr= null;
try{
int origWidth = original.getWidth();
int origHeight = original.getHeight();
if(origWidth <= newmaxWidth && origHeight <= newmaxWidth){
Bitmap b = Bitmap.createBitmap(original);
if (recycleOriginal && (original != b))
original.recycle();
return b;
}
int newWidth = 0;
int newHeight = 0;
float ratio;
if(origWidth > origHeight){
ratio = (float)origWidth/(float)origHeight;
newWidth = newmaxWidth;
newHeight = (int)((float)newWidth/ratio);
}
else{
ratio = (float)origHeight/(float)origWidth;
newHeight = newmaxHeight;
newWidth = (int)((float)newHeight/ratio);
}
rtr = CreateScaledBitmap(original , newWidth , newHeight);
if(recycleOriginal && original != rtr)
original.recycle();
}
catch (Exception e) {
Log.d("Image Compress Error", e.getMessage());
}
return rtr;
}
public static Bitmap CreateScaledBitmap(Bitmap paramBitmap, int paramInt1, int paramInt2)
{
Bitmap localBitmap = Bitmap.createBitmap(paramInt1, paramInt2, paramBitmap.getConfig());
Canvas localCanvas = new Canvas(localBitmap);
localCanvas.setDrawFilter(new PaintFlagsDrawFilter(0, 2));
localCanvas.drawBitmap(paramBitmap, new Rect(0, 0, paramBitmap.getWidth(), paramBitmap.getHeight()),
new Rect(0, 0, paramInt1, paramInt2), null);
return localBitmap;
}
Now after getting the image i have passed the following parameters to the paint.
tempPaint.setFilterBitmap(true); //Line 1
canvas.drawBitmap(currentBitmap, m_matrix, tempPaint);
You have to use Matrix . post scale to scale the image, like in the example below.
private void drawMatrix(){
Matrix matrix = new Matrix();
matrix.postScale(curScale, curScale);
canvas.drawBitmap(currentBitmap, matrix, tempPaint);}
Haven't tried it yet.... But should work...

Categories

Resources