Rotation de l'aperçu de la caméra pour Portrait Caméra Android OpenCV
J'essaie D'utiliser OpenCV 2.4.3.2 pour créer une application caméra et faire du traitement opencv. J'aimerais qu'il soit en mesure d'avoir plusieurs orientations de L'IU, pas seulement un paysage.
le problème est que lorsque je change l'orientation vers le portrait, l'image sort de côté.
je comprends que je pouvais juste tourner l'image d'entrée avant de faire le traitement d'image (et donc laisser l'orientation comme paysage seulement), ce qui est bien et fonctionne, mais ne résout pas le problème que le reste de mon UI sera dans le mauvais sens.
j'ai aussi essayé d'utiliser ce code pour faire tourner la caméra 90deg, mais cela ne semble pas fonctionner.
mCamera.setDisplayOrientation(90);
soit il n'a pas d'effet, soit il provoque juste parfois l'aperçu d'être masqué
Quelqu'un a-t-il réussi avec OpenCV? Ma classe s'étend de JavaCameraView.
Modifier
j'ai fait une amélioration, qui est que j'ai fait tourner l'image à L'intérieur D'OpenCV comme il est affiché dans la CameraBridgeViewBase.la classe java.
dans la méthode deliver and draw frame:
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());
... Fondamentalement, cela roate juste l'image d'entrée comme si
C'est mieux, mais évidemment je veux que ce soit en plein écran.
11 réponses
j'ai eu le même problème en essayant de mettre en œuvre OpenCV. J'ai pu le corriger en apportant les modifications suivantes à la méthode deliverAndDrawFrame.
-
rotation de l'objet de toile
Canvas canvas = getHolder().lockCanvas(); // Rotate canvas to 90 degrees canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2); -
redimensionnez le bitmap pour l'adapter à la taille entière de la toile avant le dessin
// 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); // Unlock canvas getHolder().unlockCanvasAndPost(canvas);
en fait,vous pouvez juste faire la largeur ou la hauteur math parent(plein écran).
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);
...
aussi,vous pouvez faire la taille de l'aperçu plus grand que l'écran.Juste modifier l'échelle.
j'ai modifié la CameraBridgeViewBase.java comme suit:
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;
}
et dans la fonction "deliver etdrawframe":
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);
}
où rotame est défini comme suit:
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;
}
le FPS de prévisualisation est plus lent parce que pour les frais généraux de calcul par rapport au mode paysage.
malheureusement Opencv4Android ne supporte pas la caméra portrait. Mais il y a un moyen de le surmonter.
1) Ecrivez votre appareil photo personnalisé et réglez son orientation sur portrait.
2) Inscrivez-vous à son rappel de prévisualisation.
3) dans onPreviewFrame(byte[]data, Camera camera) créer Mat des octets de preview:
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 dépend d'un format de prévisualisation que votre appareil utilise.
PS. n'oubliez pas de libérer toutes les instances Mat que vous avez créées lorsque vous avez terminé.
PPS. il est bon de gérer votre caméra sur un thread séparé afin de ne pas surcharger le thread tout en faisant de la détection.
j'ai le même problème, j'ai eu de comprendre !! et voici ma solution:
comme partie de first ,dans CameraBridgeViewBase.Java ,les deux constructeurs, ajoutent l'initialisation de 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();
}
ensuite, vous devez remplacer la fonction deliverAndDrawFrame(CvCameraViewFrame frame) comme suit,
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);
}
}
}
et ajouter cette fonction extra,
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;
}
}
c'est okey ,et Si cette réponse utile pour vous, s'il vous plaît marquer 'accepté' l'Aide de la Réputation de
toutes les réponses ici sont des hacks. je préfère cette solution :
changement de code JavaCameraView:
mBuffer = new byte[size];
mCamera.setDisplayOrientation(90); //add this
mCamera.addCallbackBuffer(mBuffer);
Deuxième Changement:
// 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());
il semble que la nouvelle classe OpenCV CameraBridgeViewBase.java est de trop haut niveau et ne donne pas assez de contrôle sur la disposition de l'aperçu de la caméra. Jetez un oeil à mon code échantillon , qui est basé sur certains des anciens échantillons OpenCV et utilise le code Android pur. Pour utiliser le tableau de byte passé dans onPreviewFrame , put() il dans un tapis et convertir de YUV en RGB:
mYuv = new Mat(previewHeight + previewHeight/2, previewWidth, CvType.CV_8UC1);
mYuv.put(0, 0, mBuffer);
Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGBA, 4);
vous pouvez être en mesure de trouver les anciens échantillons OpenCV4Android sur le internet, bien qu'ils aient été retirés il y a quelques versions. Cependant, le code échantillon lié et l'extrait ci-dessus devraient être suffisants pour vous permettre de commencer.
si vous utilisez l'openCV 2.4.9, essayez :
1) copier le contenu du tutoriel opencv-mixed processing dans votre code;
2) corriger les erreurs d'appariement (nom de l'activité et probablement référence de la mise en page);
3) modifier votre manifeste en ajoutant android:screenOrientation ="landscape"
4) corriger les erreurs mineures et courir !!!! bbaamm (il devrait fonctionner correctement maintenant)
Note: avec cette méthode la barre d'état apparaît sur le côté droit lorsque le téléphone est en position portrait . Puisque nous développons la caméra projet, je vous conseille de supprimer la barre d'état de l'aperçu.
Espère que cela aide !!!
j'ai l'orientation portrait avec CameraBridgeViewBase, mais j'ai dû changer JavaCameraView.de java à l'intérieur de la OpenCV :( L'idée est la suivante: après la caméra init, faire
setDisplayOrientation(mCamera, 90);
mCamera.setPreviewDisplay(getHolder());
et méthode de placementorientation
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)
{
}
}
Vous devez considérer quelques petites choses:
- onPreviewFrame() fournit toujours les premières données de la caméra dans son assambled rotation
- getSupportedPreviewSizes () donne les rapports d'aspect correspondants
- algorithme a besoin d'analyser le cadre dans le portrait pour détecter les objets corrects.
- le Bitmap créé (côté Java) pour stocker le cadre résultant a également besoin du bon format
ainsi, pour une solution rapide et haute résolution J'ai changé JavaCameraView.java et mon JNI-part. à 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);
avec ces modifications, nous nous sommes assurés d'utiliser le résultat le plus élevé disponible pour portrait (commutateurs hauteur/largeur en calculateCameraFrameSize). Nous sommes toujours en train de gérer landscape en tant qu'input à partir de onPreviewFrame() mais nous avons créé un Bitmap pour dessiner en portrait (AllocateCache).
enfin, nous devons donner l'algorithme the portrait-frame afin de lui permettre de détecter les objets "debout" et de le retourner pour sauvegarder et rendre le bitmap. Ainsi, les modifications suivantes à votre activité:
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);
...
"jaiprakashgogi" réponse développeur travaille pour moi. mais le problème, c'est l'aperçu enregistré comme paysage. cela signifie que si nous définissons l'aperçu à imageview alors il s'affiche comme paysage.
la solution ci-dessus fonctionne jusqu'à montrer l'aperçu comme portrait mais pas enregistré comme portrait de façon persistante.
j'ai été résolu que la question de la façon suivante.
- convertissez les données byte ou mat en bitmap
- tourner la matrice à 90 degrés et appliquer à bitmap
- convertissez bitmap en tableau octet et enregistrez-le.
s'il vous plaît voir mon code ici...
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;
}
ça résout complètement mon problème.