2013-02-11 2 views
26

Я пытаюсь использовать OpenCV 2.4.3.2 для создания приложения для камеры и обработки некоторых opencv. Я хотел бы, чтобы он мог иметь несколько ориентаций пользовательского интерфейса, а не только Landscape.Поверните предварительный просмотр камеры в портретную камеру Android OpenCV

Проблема в том, что при изменении ориентации на портрет изображение выходит сбоку.

Я понимаю, что I could just rotate the input image перед обработкой изображения (и, таким образом, оставить ориентацию только ландшафтом), что прекрасно и работает, но не решает проблему, что остальная часть моего интерфейса будет в неправильной ориентации.

Я также попытался использовать this code для поворота камеры 90deg, но он просто не работает.

mCamera.setDisplayOrientation(90); 

Он либо не имеет никакого эффекта, а иногда просто вызывает предварительный просмотр, чтобы быть затемнены

Кто-нибудь делал это успешно с OpenCV? Мой класс распространяется из JavaCameraView. portrait image with sideways preview

Редактировать

Я сделал улучшение, которое я вращался изображение внутри OpenCV, как он отображается в классе CameraBridgeViewBase.java.

В способе доставки и сделать кадр:

if (canvas != null) { 
      canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); 
      //canvas.drawBitmap(mCacheBitmap, (canvas.getWidth() - mCacheBitmap.getWidth())/2, (canvas.getHeight() - mCacheBitmap.getHeight())/2, null); 
      //Change to support portrait view 
      Matrix matrix = new Matrix(); 
      matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth())/2,(canvas.getHeight() - mCacheBitmap.getHeight())/2); 

      if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) 
       matrix.postRotate(90f,(canvas.getWidth())/2,(canvas.getHeight())/2); 
      canvas.drawBitmap(mCacheBitmap, matrix, new Paint()); 

... В принципе, это просто roatates входное изображение как так

input image rotated 90

Это лучше, но я хочу, очевидно, это будет полный экран.

+0

Почему не просто установить screenorientation на Android? –

+0

Я просто добавил изображение, чтобы объяснить поведение, я раньше не объяснял его. Я фактически настраиваю ориентацию на портрет – Jameo

+0

Является ли ваш код основанный на образцах андроида opencv? Я настоятельно рекомендую вам создать код поверх одного из этих образцов, потому что его код уже исправляет проблемы с камерой, которые могут возникнуть в будущем. –

ответ

1

Кажется, что новый класс OpenCV CameraBridgeViewBase.java слишком высокоуровневый и не дает достаточного контроля над расположением предварительного просмотра камеры. Взгляните на мой sample code, который основан на некоторых старых образцах OpenCV и использует чистый код Android. Чтобы использовать массив байт, переданный в onPreviewFrame, put() его в циновку и конвертировать из YUV в RGB:

mYuv = new Mat(previewHeight + previewHeight/2, previewWidth, CvType.CV_8UC1); 
mYuv.put(0, 0, mBuffer); 
Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGBA, 4); 

Вы можете быть в состоянии найти старые OpenCV4Android образцов в Интернете, хотя они были вырваны несколько версий тому назад. Однако связанный примерный код и фрагмент выше должны быть достаточно, чтобы вы начали.

1

Если вы используете openCV 2.4.9, попробуйте: 1) скопировать содержимое opencv tutorial-mixed обработки в ваш код; 2) исправить ошибки несоответствия (название операции и, возможно, ссылку на макет); 3) Измените свой манифест, добавив android:screenOrientation ="landscape" 4) исправить ошибки несовершеннолетних и запустить !!!! bbaamm (теперь он должен работать должным образом)

Примечание: с помощью этого метода панель состояния появляется справа, когда телефон находится в портретном положении. Поскольку мы разрабатываем проект камеры, я советую вам удалить строку состояния из предварительного просмотра.

Надеюсь, что это поможет !!!

8

Фактически, вы можете просто создать родительскую форму ширины или высоты (полный экран).

if (canvas != null) { 
     canvas.rotate(90,0,0); 
     scale = canvas.getWidth()/(float)bitmap.getHeight(); 
     float scale2 = canvas.getHeight()/(float)bitmap.getWidth(); 
     if(scale2 > scale){ 
      scale = scale2; 
     } 
     if (scale != 0) { 
      canvas.scale(scale, scale,0,0); 
     } 
     canvas.drawBitmap(bitmap, 0, -bitmap.getHeight(), null); 

...

Кроме того, вы можете сделать размер предварительного просмотра больше, чем screen.Just изменить масштаб.

0

I`ve получил портретную ориентацию CameraBridgeViewBase, но я должен был изменить JavaCameraView.java внутри OpenCV :(Идея заключается в следующем: после запуска камеры, сделайте следующий

setDisplayOrientation(mCamera, 90); 
mCamera.setPreviewDisplay(getHolder()); 

и setDisplayOrientation методу

protected void setDisplayOrientation(Camera camera, int angle){ 
    Method downPolymorphic; 
    try 
    { 
     downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class }); 
     if (downPolymorphic != null) 
      downPolymorphic.invoke(camera, new Object[] { angle }); 
    } 
    catch (Exception e1) 
    { 
    } 
} 
+0

Привет, я также сталкиваюсь с проблемой ориентации. могли бы более подробно описать ваше решение? я попытался добавить (JavaCamerView) setDisplayOrientation (mCamera, 90); mCamera.setPreviewDisplay (getHolder()); если (! InitializeCamera (ширина, высота)) возвращение false; Однако это приводит к черному экрану предварительного просмотра. –

+2

Этот метод корректирует ориентацию для меня, но просмотр обратного вызова перестает работать, и он также продолжает бросать следующее исключение: –

+0

E/SurfaceHolder: Исключительная блокировка поверхности java.lang.IllegalArgumentException –

7

Я изменил CameraBridgeViewBase.java следующим образом:

protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) { 
    int calcWidth = 0; 
    int calcHeight = 0; 

    if(surfaceHeight > surfaceWidth){ 
     int temp = surfaceHeight; 
     surfaceHeight = surfaceWidth; 
     surfaceWidth = temp; 
    } 

И в функции «deliverAndDrawFrame»:

  if (mScale != 0) { 
       if(canvas.getWidth() > canvas.getHeight()) { 
       canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()), 
        new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth())/2), 
        (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight())/2), 
        (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth())/2 + mScale*mCacheBitmap.getWidth()), 
        (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight())/2 + mScale*mCacheBitmap.getHeight())), null); 
       } else { 
        canvas.drawBitmap(mCacheBitmap, rotateMe(canvas, mCacheBitmap), null); 
       } 

где rotateMe определяются следующим образом:

private Matrix rotateMe(Canvas canvas, Bitmap bm) { 
    // TODO Auto-generated method stub 
    Matrix mtx=new Matrix(); 
    float scale = (float) canvas.getWidth()/(float) bm.getHeight(); 
    mtx.preTranslate((canvas.getWidth() - bm.getWidth())/2, (canvas.getHeight() - bm.getHeight())/2); 
    mtx.postRotate(90,canvas.getWidth()/2, canvas.getHeight()/2); 
    mtx.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2); 
    return mtx; 
} 

Предварительного FPS медленнее, потому что для вычислительных накладных расходов по сравнению с ландшафтным режимом.

+0

Отлично работает, большое вам спасибо! – HanClinto

+3

это работает аккуратно. однако пример обнаружения лица, который я использую, затем не распознает лица. он предполагает ландшафтный режим. любые идеи по этому поводу? thx –

+0

Это работает, должен быть принят ответ. –

0

Вы должны рассмотреть несколько вещей:

  • onPreviewFrame() всегда поставляет исходные данные камеры в assambled вращения
  • getSupportedPreviewSizes() дает соответствующие соотношения сторон
  • алгоритм должен проанализировать кадр в портрете для правильного обнаружения объектов.
  • битовая карта создается (Java-сторона) для хранения результирующий кадр также нуждается в правильной пропорции

Таким образом, для быстрой и высокой разрешающей способностью решения я изменил JavaCameraView.java и мой JNI-часть. в JavaCameraView.java:

... 

     if (sizes != null) { 
     /* Select the size that fits surface considering maximum size allowed */ 
     Size frameSize; 
     if(width > height) 
     { 
      frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height); 
     }else{ 
      frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), height, width); 
     } 
... 

     mCamera.setParameters(params); 
     params = mCamera.getParameters(); 

     int bufFrameWidth, bufFrameHeight; 
     bufFrameWidth = params.getPreviewSize().width; 
     bufFrameHeight = params.getPreviewSize().height; 

     if(width > height) { 
      mFrameWidth = params.getPreviewSize().width; 
      mFrameHeight = params.getPreviewSize().height; 
     }else{ 
      mFrameWidth = params.getPreviewSize().height; 
      mFrameHeight = params.getPreviewSize().width; 
     } 
... 

     mFrameChain = new Mat[2]; 
     mFrameChain[0] = new Mat(bufFrameHeight + (bufFrameHeight/2), bufFrameWidth, CvType.CV_8UC1); 
     mFrameChain[1] = new Mat(bufFrameHeight + (bufFrameHeight/2), bufFrameWidth, CvType.CV_8UC1); 

     AllocateCache(); 

     mCameraFrame = new JavaCameraFrame[2]; 
     mCameraFrame[0] = new JavaCameraFrame(mFrameChain[0], bufFrameWidth, bufFrameHeight); 
     mCameraFrame[1] = new JavaCameraFrame(mFrameChain[1], bufFrameWidth, bufFrameHeight); 

С учетом этих изменений, мы убедились, что мы используем самую высокую resultion доступную для портретной (переключатели высота/ширина в calculateCameraFrameSize). Мы по-прежнему обрабатываем ландшафт в качестве входных данных из onPreviewFrame(), но создали Bitmap для рисования в портрете (AllocateCache).

Последнее, нам нужно дать алгоритму портретный кадр, чтобы он обнаруживал «стоящие» объекты и возвращал его для сохранения и рендеринга растрового изображения. Так следующие изменения в вашей деятельности:

public Mat rot90(Mat matImage, int rotflag){ 
    //1=CW, 2=CCW, 3=180 
    Mat rotated = new Mat(); 
    if (rotflag == 1){ 
     rotated = matImage.t(); 
     flip(rotated, rotated, 1); //transpose+flip(1)=CW 
    } else if (rotflag == 2) { 
     rotated = matImage.t(); 
     flip(rotated, rotated,0); //transpose+flip(0)=CCW 
    } else if (rotflag ==3){ 
     flip(matImage, rotated,-1); //flip(-1)=180 
    } else if (rotflag != 0){ //if not 0,1,2,3: 
     Log.e(TAG, "Unknown rotation flag("+rotflag+")"); 
    } 
    return rotated; 
} 

public Mat onCameraFrame(CvCameraViewFrame inputFrame) { 

    mRgba = rot90(inputFrame.rgba(), 1); 
    mGray = rot90(inputFrame.gray(), 1); 
... 
9

У меня была та же проблема, пытаясь реализовать OpenCV. Я смог исправить это, внеся следующие изменения в метод deliverAndDrawFrame.

  1. Поворот объекта холста

    Canvas canvas = getHolder().lockCanvas(); 
    // Rotate canvas to 90 degrees 
    canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2); 
    
  2. изменить размер точечного рисунка, чтобы соответствовать всему размеру холста перед нанесением

    // Resize 
    Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true); 
    // Use bitmap instead of mCacheBitmap 
    canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect((int)((canvas.getWidth() - mScale*bitmap.getWidth())/2),(int)((canvas.getHeight() - mScale*bitmap.getHeight())/2),(int)((canvas.getWidth() - mScale*bitmap.getWidth())/2 + mScale*bitmap.getWidth()), (int) ((canvas.getHeight() - mScale*bitmap.getHeight())/2 + mScale*bitmap.getHeight())), null); 
    
+0

Wrboleski, проблема в том, что при использовании FeatureDetector это не работает. Детектор функций теперь просматривает изображение сбоку. –

4

К сожалению Opencv4Android не поддерживает портретную камеру. Но есть способ преодолеть это. 1) Напишите свою собственную камеру и установите ориентацию на портрет. 2) Зарегистрируйтесь для предварительного просмотра. 3) В onPreviewFrame(byte[]data, Camera camera) создать Mat предпросмотра байт:

Mat mat = new Mat(previewSize.height, previewSize.width, CvType.CV_8UC1); 
mat.put(0, 0, data); 

Core.transpose(mat, mat); 
Core.flip(mat, mat, -1); // rotates Mat to portrait 

CvType зависит от формата просмотра камера использует.

PS. не забудьте выпустить все экземпляры Mat, которые вы создали, когда закончите.

PPS. хорошо управлять своей камерой на отдельном потоке, чтобы не перегружать поток пользовательского интерфейса при некотором обнаружении.

0

Ответ разработчика «jaiprakashgogi» работает для меня. но проблема заключается в том, что предварительный просмотр сохраняется только как пейзаж. это означает, что если мы установим предварительный просмотр в изображение, то он будет отображаться как пейзаж.

Вышеупомянутое решение работает до отображения предварительного просмотра как портрет, но не сохраняется как портрет настойчиво.

Я решил эту проблему следующим образом.

  1. преобразовать данные байт или мата в битовой карте
  2. вращать матрицу на 90 градусов и применять в растровые
  3. конвертировать растровое изображение в массив байтов и сохранить его.

смотрите в мой код здесь ...

public String writeToSDFile(byte[] data, int rotation){ 


    byte[] portraitData=null; 

    if(rotation==90){ 
     Log.i(TAG,"Rotation is : "+rotation); 
     Bitmap bitmap= BitmapFactory.decodeByteArray(data,0,data.length); 
     Matrix matrix = new Matrix(); 

     matrix.postRotate(90); 

     Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); 
    portraitData=bitmapToByte(rotatedBitmap); 


    } 

    File dir=getDirectory(); 
    String imageTime=""+System.currentTimeMillis(); 

    String fileName=Constants.FILE_NAME+imageTime+"."+Constants.IMAGE_FORMAT; 
    File file = new File(dir, fileName); 

    try { 
     FileOutputStream f = new FileOutputStream(file); 

     if(rotation==90){ 
      f.write(portraitData); 
     }else { 
      f.write(data); 
     } 

     f.close(); 
    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
     Log.i(TAG, "******* File not found. Did you" + 
       " add a WRITE_EXTERNAL_STORAGE permission to the manifest?"); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    Log.i(TAG,"\n\nFile written to "+file); 

    return fileName; 
} 

// convert bitmap to Byte Array 

    public byte[] bitmapToByte(Bitmap bitmap){ 

    ByteArrayOutputStream outputStream=new ByteArrayOutputStream(); 
    bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream); 

    byte[] array=outputStream.toByteArray(); 
    return array; 
} 

Это полностью решает мою проблему.

0

У меня такая же проблема, у меня есть выяснение!и есть мое решение:

как часть первого, в CameraBridgeViewBase.Java, два конструктора, добавить инициализацию WindowManager:

public CameraBridgeViewBase(Context context, int cameraId) { 
    super(context); 
    mCameraIndex = cameraId; 
    getHolder().addCallback(this); 
    mMaxWidth = MAX_UNSPECIFIED; 
    mMaxHeight = MAX_UNSPECIFIED; 
    windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 

}

public CameraBridgeViewBase(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    int count = attrs.getAttributeCount(); 
    Log.d(TAG, "Attr count: " + Integer.valueOf(count)); 

    TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs, R.styleable.CameraBridgeViewBase); 
    if (styledAttrs.getBoolean(R.styleable.CameraBridgeViewBase_show_fps, false)) 
     enableFpsMeter(); 

    mCameraIndex = styledAttrs.getInt(R.styleable.CameraBridgeViewBase_camera_id, -1); 

    getHolder().addCallback(this); 
    mMaxWidth = MAX_UNSPECIFIED; 
    mMaxHeight = MAX_UNSPECIFIED; 
    windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
    styledAttrs.recycle(); 

}

тогда, Вам необходимо заменить функцию deliverAndDrawFrame(CvCameraViewFrame frame) следующим образом:

protected void deliverAndDrawFrame(CvCameraViewFrame frame) { 
    Mat modified; 

    if (mListener != null) { 
     modified = mListener.onCameraFrame(frame); 
    } else { 
     modified = frame.rgba(); 
    } 

    boolean bmpValid = true; 
    if (modified != null) { 
     try { 
      Utils.matToBitmap(modified, mCacheBitmap); 
     } catch (Exception e) { 
      Log.e(TAG, "Mat type: " + modified); 
      Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight()); 
      Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage()); 
      bmpValid = false; 
     } 
    } 

    if (bmpValid && mCacheBitmap != null) { 
     Canvas canvas = getHolder().lockCanvas(); 
     if (canvas != null) { 
      canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); 
      int rotation = windowManager.getDefaultDisplay().getRotation(); 
      int degrees = 0; 
      // config degrees as you need 
      switch (rotation) { 
       case Surface.ROTATION_0: 
        degrees = 90; 
        break; 
       case Surface.ROTATION_90: 
        degrees = 0; 
        break; 
       case Surface.ROTATION_180: 
        degrees = 270; 
        break; 
       case Surface.ROTATION_270: 
        degrees = 180; 
        break; 
      } 

      Matrix matrix = new Matrix(); 
      matrix.postRotate(degrees); 
      Bitmap outputBitmap = Bitmap.createBitmap(mCacheBitmap, 0, 0, mCacheBitmap.getWidth(), mCacheBitmap.getHeight(), matrix, true); 

      if (outputBitmap.getWidth() <= canvas.getWidth()) { 
       mScale = getRatio(outputBitmap.getWidth(), outputBitmap.getHeight(), canvas.getWidth(), canvas.getHeight()); 
      } else { 
       mScale = getRatio(canvas.getWidth(), canvas.getHeight(), outputBitmap.getWidth(), outputBitmap.getHeight()); 
      } 

      if (mScale != 0) { 
       canvas.scale(mScale, mScale, 0, 0); 
      } 
      Log.d(TAG, "mStretch value: " + mScale); 

      canvas.drawBitmap(outputBitmap, 0, 0, null); 

      if (mFpsMeter != null) { 
       mFpsMeter.measure(); 
       mFpsMeter.draw(canvas, 20, 30); 
      } 
      getHolder().unlockCanvasAndPost(canvas); 

     } 
    } 

}

и добавить этот Funtion дополнительный,

private float getRatio(int widthSource, int heightSource, int widthTarget, int heightTarget) { 
    if (widthTarget <= heightTarget) { 
     return (float) heightTarget/(float) heightSource; 
    } else { 
     return (float) widthTarget/(float) widthSource; 
    } 

}

это ОКЕЙ, и если этот ответ полезным для вас, пожалуйста, отметьте 'принят' Помощь Репутация

2

Все ответы здесь - хаки. я предпочитаю это решение:

изменение JavaCameraView кода:

mBuffer = new byte[size]; 
mCamera.setDisplayOrientation(90); //add this 
mCamera.addCallbackBuffer(mBuffer); 

Второе изменение:

//     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
//      mSurfaceTexture = new SurfaceTexture(MAGIC_TEXTURE_ID); 
//      mCamera.setPreviewTexture(mSurfaceTexture); 
//     } else 
//      mCamera.setPreviewDisplay(null); 
        mCamera.setPreviewDisplay(getHolder()); 
+0

удаляет рамку обнаружения зеленого лица –