Zxing Camera in Portrait mode on Android
Asked Answered
M

9

42

I want to show portrait orientation on Zxing's camera.

How can this be done?

Mommy answered 27/4, 2013 at 13:38 Comment(1)
Here is the solutionExpellant
M
108

Here's how it works.

Step 1: Add following lines to rotate data before buildLuminanceSource(..) in decode(byte[] data, int width, int height)

DecodeHandler.java:

byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++)
        rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;

PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height);

Step 2: Modify getFramingRectInPreview().

CameraManager.java

rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

Step 3: Disable the check for Landscape Mode in initFromCameraParameters(...)

CameraConfigurationManager.java

//remove the following
if (width < height) {
  Log.i(TAG, "Display reports portrait orientation; assuming this is incorrect");
  int temp = width;
  width = height;
  height = temp;
}

Step 4: Add following line to rotate camera insetDesiredCameraParameters(...)

CameraConfigurationManager.java

camera.setDisplayOrientation(90);

Step 5: Do not forget to set orientation of activity to portrait. I.e: manifest

Mommy answered 27/4, 2013 at 13:54 Comment(33)
This is pretty good! although you're going from only supporting landscape to only supporting portrait, rather than both. This won't work for front cameras either, or the upside-down portrait orientation.Singapore
@SeanOwen Yes sir, you are right :) It just a very simple tweak on the library to have a portrait-only camera scanning.Mommy
for me it doesn't works. My view finder rectangle in landScape mode but the camera rotate in 180. what goes wrong?Fascinator
The camera images are squashed/not in proportion after going portrait. It scans fine though. Any way to fix this?Hostetter
this exception shows FATAL EXCEPTION: Thread-9 java.lang.IllegalArgumentException: Crop rectangle does not fit within image data. : at com.google.zxing.client.android.PlanarYUVLuminanceSource.<init>(PlanarYUVLuminanceSource.java:46) : at com.google.zxing.client.android.camera.CameraManager.buildLuminanceSource(CameraManager.java:275) at com.google.zxing.client.android.DecodeHandler.decode(DecodeHandler.java:80) E/AndroidRuntime(5523): at com.google.zxing.client.android.DecodeHandler.handleMessage(DecodeHandler.java:54)Ingenuity
@Ingenuity How is it going now? DId you follow all the stesps?Mommy
@Roylee , hey thnks , it is working, i want to ask, can it work in both landscap and portrait according to sensor, please help if u canIngenuity
@Ingenuity Do you meant if you could have both landscape and portrait? Not with this code snippet :)Mommy
@Ingenuity I haven't tried yet and I didn't touched this project for quite sometime. Are you able to find a solution in SO, maybe you could have opened a thread?Mommy
This rotates the camera etc but then it doesnt scan for me.Supen
@RussellCargill Are you following the steps correctly? it works for others.Mommy
Yeah I followed them to the letter but it would never scan for me :S I found a solution though. Don't rotate it haha! I'm using it for QR Codes so it works both ways so I shall just remove any text from the view and leave it at that! But thanks for the concern.Supen
worked to me, although camera image is a bit out of aspect ratio (qrcode, nexus4)Standoff
I´m having problems when running in ldpi device (Samsung Galaxy Y GT-S5360), the camera image stays wrong... did anyone experienced this?Standoff
But qr code is not being scanned proper as landscape modeLysias
This is outdated with current version of ZXing libraryLaminate
If it doers not scan after rotate, I suspect step 1 is not followed PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height); Remember to replace data with "rotatedData". The text at step 1 is a bit misleading. One could think to only add the code, not replace the data variable.Emergency
Very helpful, since I'm still holding on to ZXing v2.2 for legacy compatibility. I made it auto-detect the orientation -- instead of hard-coding portrait mode, I verify that the orientation is Configuration.ORIENTATION_PORTRAIT before performing these alternate operations. I also implemented a rotation calculation method as described in the comments for Camera.setDisplayOrientation(). Now I can rotate at will and the image appears correct (and it scans).Systematics
@Laminate i just tried the 3.1.0 version, and couldn't see any convenient way to achieve portrait mode for barcodesRepose
@Su-AuHwang Exacly, gotta work with legacy libs or set down spending hours to reverse engineer =/Laminate
@Laminate what I meant was: I don't see how this answer is outdated. It still works on 3.10 and 3.10 still doesn't offer a more convenient way.Repose
@Su-AuHwang Okay, I might've done something wrong, at least this fix didn't work for me with current version of ZXing library. The library has also been changed a lot since this was made.Laminate
my zxing version is 3.0.1, and the above changes seem do not apply to my case. I cannot have the camera in portrait mode. any helps? TIACottonweed
May I ask, for Step4, where should I put the code in?Cottonweed
@Cottonweed CameraConfigurationManager.javaMommy
It works finally, but how could I change the dimension of the small rectangle inside the barcode scanner (the non-shady region)?Cottonweed
The image captured by camera is rotated by 90 degree while i am trying to scan. Like if you are clicking a photo of a person using the camera, then in my phone screen is showing the preview rotated by 90 degree. But that is not what i want as it is making bar code scanning difficult to use. I want preview as it should be. 1) So 1st of all is it like that what you guys are getting it work too or i have done something wrong? 2) If it is like so is there any way to make it normal user friendly?Tuppeny
need to fix findBestPreviewSizeValue() method in CameraConfigurationManager.java, too: find int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; and edit to: int maybeFlippedHeight = isCandidatePortrait ? realHeight : realWidth; int maybeFlippedWidth = isCandidatePortrait ? realWidth : realHeight;Scupper
If someone's looking for supporting both portrait and landscape mode, check this answer : https://mcmap.net/q/358703/-zxing-camera-portrait-mode-and-landscape-on-androidEpileptic
Hi, did you idea for zxing new version lib (3.0). it work with old versionLexicology
@SameerZ. Unfortunately, not at the moment. It's been awhile since my last programming with zxing lib. Why not find out yourself and then post it here, if its correct, I will definitely mark it as answer :)Mommy
@Roylee github.com/xiaowei4895/zxing-android-portrait finally it is workingLexicology
works well, but the yellow result points are not shown on the barcode.Interrex
M
7

To support all orientation and change automatically when rotating the activity do this, all you have to modify is the CameraManager.javaclass.

And remove this method getCurrentOrientation() from CaptureActivity.java

In CameraManager.java Create this variable:

int resultOrientation;

Add this to the openDriver(..) method:

setCameraDisplayOrientation(context, Camera.CameraInfo.CAMERA_FACING_BACK, theCamera);//this can be set after camera.setPreviewDisplay(); in api13+.

****Create this method**** Link: http://developer.android.com/reference/android/hardware/Camera.html

public static void setCameraDisplayOrientation(Context context,int cameraId, android.hardware.Camera camera) {
    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    int degrees = 0;
    switch (display.getRotation()) {
    case Surface.ROTATION_0: degrees = 0; break;
    case Surface.ROTATION_90: degrees = 90; break;
    case Surface.ROTATION_180: degrees = 180; break;
    case Surface.ROTATION_270: degrees = 270; break;
    }


    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        resultOrientation = (info.orientation + degrees) % 360;
        resultOrientation = (360 - resultOrientation) % 360;  // compensate the mirror
    } else {  // back-facing
        resultOrientation = (info.orientation - degrees + 360) % 360;
    }
    camera.setDisplayOrientation(resultOrientation);
}

****Now modify getFramingRectInPreview()****

if(resultOrientation == 180 || resultOrientation == 0){//to work with landScape and reverse landScape
            rect.left = rect.left * cameraResolution.x / screenResolution.x;
            rect.right = rect.right * cameraResolution.x / screenResolution.x;
            rect.top = rect.top * cameraResolution.y / screenResolution.y;
            rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
        }else{
            rect.left = rect.left * cameraResolution.y / screenResolution.x;
            rect.right = rect.right * cameraResolution.y / screenResolution.x;
            rect.top = rect.top * cameraResolution.x / screenResolution.y;
            rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
        }

And modify this method public PlanarYUVLuminanceSource buildLuminanceSource(..)

if(resultOrientation == 180 || resultOrientation == 0){//TODO: This is to use camera in landScape mode
        // Go ahead and assume it's YUV rather than die.
        return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
    }else{
        byte[] rotatedData = new byte[data.length];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++)
                rotatedData[x * height + height - y - 1] = data[x + y * width];
        }
        int tmp = width;
        width = height;
        height = tmp;
        return new PlanarYUVLuminanceSource(rotatedData, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
    }
Mishap answered 6/2, 2015 at 20:34 Comment(2)
it breaks the scale ratio of the preview on some devicesInterrex
@Interrex I believe that's why they don't support the rotation in their own version of the library.Mishap
N
5

You can use my fork of zxlib https://github.com/rusfearuth/zxing-lib-without-landscape-only. I disabled landscape mode only. You can set landscape/portrait and see correct camera view.

Nisen answered 6/9, 2013 at 3:59 Comment(0)
M
3

Adding camera.setDisplayOrientation(90); in CameraConfigurationManager.java worked for me.

Metalloid answered 10/9, 2015 at 10:5 Comment(2)
where to make this change ?Nedrud
i think we need to edit the source code of zxing am i right @Samit?Wiretap
L
2

for zxing 3.0, working lib https://github.com/xiaowei4895/zxing-android-portrait for portrait mode

Thank you

Lexicology answered 14/5, 2015 at 10:3 Comment(0)
T
2

I think the best library only solution is this one ...

https://github.com/SudarAbisheck/ZXing-Orient

You can include it in build.gradle as a dependency of your project in maven format ...

dependencies {
  compile ''me.sudar:zxing-orient:2.1.1@aar''
}
Tentage answered 30/5, 2016 at 3:30 Comment(2)
This library does not work. Jsut does not scan, all I get is QRCodeNotFoundOnCamImageNedrud
@Nedrud thanks yes I've edited my answer for a different library that works well, in fact the same way as ZXing BarcodeScanner and BarcodeScannerPlus apps.Tentage
W
2

Create AnyOrientationCaptureActivity and then override default CaptureActivity then it will work.

public void scanCode() {
    IntentIntegrator integrator = new IntentIntegrator(this);
    integrator.setDesiredBarcodeFormats(CommonUtil.POSEIDON_CODE_TYPES);
    integrator.setPrompt("Scan");
    integrator.setCameraId(0);
    integrator.setBeepEnabled(false);
    integrator.setBarcodeImageEnabled(false);
    integrator.setOrientationLocked(false);
    //Override here
    integrator.setCaptureActivity(AnyOrientationCaptureActivity.class);

    integrator.initiateScan();
}

//create AnyOrientationCaptureActivity extend CaptureActivity
public class AnyOrientationCaptureActivity extends CaptureActivity {
}

Define in manifest

<activity
            android:name=".views.AnyOrientationCaptureActivity"
            android:screenOrientation="fullSensor"
            android:stateNotNeeded="true"
            android:theme="@style/zxing_CaptureTheme"
            android:windowSoftInputMode="stateAlwaysHidden"></activity>
Wolffish answered 13/9, 2017 at 8:50 Comment(1)
captureactivity is a replacement of normal extends activity?Wiretap
A
1

This is supposed to be a synched version to the above solution

https://github.com/zxing/zxing/tree/4b124b109d90ac2960078ce68e15a39885fc1b5b

Aq answered 29/12, 2014 at 13:21 Comment(0)
T
1

Additionally to @roylee's modification I had to apply the following to the CameraConfigurationManager.java in order to get best possible preview and QR code recognition quality

    diff --git a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
index cd9d0d8..4f12c8c 100644
--- a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
+++ b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
@@ -56,21 +56,24 @@ public final class CameraConfigurationManager {
     Display display = manager.getDefaultDisplay();
     int width = display.getWidth();
     int height = display.getHeight();
-    // We're landscape-only, and have apparently seen issues with display thinking it's portrait 
+    // We're landscape-only, and have apparently seen issues with display thinking it's portrait
     // when waking from sleep. If it's not landscape, assume it's mistaken and reverse them:
+    /*
     if (width < height) {
       Log.i(TAG, "Display reports portrait orientation; assuming this is incorrect");
       int temp = width;
       width = height;
       height = temp;
     }
+    */
     screenResolution = new Point(width, height);
     Log.i(TAG, "Screen resolution: " + screenResolution);
-    cameraResolution = findBestPreviewSizeValue(parameters, screenResolution, false);
+    cameraResolution = findBestPreviewSizeValue(parameters, screenResolution, true);//
     Log.i(TAG, "Camera resolution: " + cameraResolution);
   }

   void setDesiredCameraParameters(Camera camera) {
+    camera.setDisplayOrientation(90);
     Camera.Parameters parameters = camera.getParameters();

     if (parameters == null) {
@@ -99,7 +102,7 @@ public final class CameraConfigurationManager {
   Point getScreenResolution() {
     return screenResolution;
   }
-  
+
   public void setFrontCamera(boolean newSetting) {
     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
     boolean currentSetting = prefs.getBoolean(PreferencesActivity.KEY_FRONT_CAMERA, false);
@@ -109,12 +112,12 @@ public final class CameraConfigurationManager {
       editor.commit();
     }
   }
-  
+
   public boolean getFrontCamera() {
     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
     return prefs.getBoolean(PreferencesActivity.KEY_FRONT_CAMERA, false);
   }
-  
+
   public boolean getTorch() {
     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
     return prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false);
@@ -181,7 +184,14 @@ public final class CameraConfigurationManager {
       Camera.Size defaultSize = parameters.getPreviewSize();
       bestSize = new Point(defaultSize.width, defaultSize.height);
     }
+
+    // FIXME: test the bestSize == null case!
+    // swap width and height in portrait case back again
+    if (portrait) {
+        bestSize = new Point(bestSize.y, bestSize.x);
+    }
     return bestSize;
+
   }

   private static String findSettableValue(Collection<String> supportedValues,
Tanika answered 30/3, 2017 at 13:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.