简体   繁体   English

使用JavaCV库旋转图像的问题

[英]Issue with image rotation using JavaCV library

I am writing an Android application which records video for a specified amount of time. 我正在编写一个Android应用程序,该程序在指定的时间内记录视频。 Everything works fine if I record using the smartphone's back camera. 如果使用智能手机的后置摄像头录制,一切都会正常。 The app has a feature to pause/record feature like in Vine app. 该应用程序具有Vine应用程序中的暂停/录制功能。 The issue comes when recording using the device's front camera. 使用设备的前置摄像头录制时出现问题。 The video surface frame looks fine when storing/playing the video the video is upside down. 当存储/播放视频时,视频表面框架看起来不错,视频是颠倒的。 There is a lot of things discussed about this issue everywhere. 到处都有很多关于此问题的讨论。 But I didn't find any solution that WORKS. 但是我没有找到任何可行的解决方案。

Have a look at the code and image mentioned below. 看一下下面提到的代码和图像。

Here is the original image taken from front camera. 这是从前置摄像头拍摄的原始图像。 I have turned it upside down for a better view. 我将其倒置以获得更好的视野。

在此处输入图片说明

Here is what I actually get after rotation: 这是轮换后我实际得到的:

在此处输入图片说明

Method: 方法:

     IplImage copy = cvCloneImage(image);
     IplImage rotatedImage = cvCreateImage(cvGetSize(copy), copy.depth(), copy.nChannels());
     //Define Rotational Matrix
     CvMat mapMatrix = cvCreateMat(2, 3, CV_32FC1);

     //Define Mid Point
     CvPoint2D32f centerPoint = new CvPoint2D32f();
     centerPoint.x(copy.width() / 2);
     centerPoint.y(copy.height() / 2);

     //Get Rotational Matrix
     cv2DRotationMatrix(centerPoint, angle, 1.0, mapMatrix);

     //Rotate the Image
     cvWarpAffine(copy, rotatedImage, mapMatrix, CV_INTER_CUBIC + CV_WARP_FILL_OUTLIERS, cvScalarAll(170));
     cvReleaseImage(copy);
     cvReleaseMat(mapMatrix);

I have tried doing 我尝试做

     double angleTemp = angle;

     angleTemp= ((angleTemp / 90)%4)*90;       
     final int number = (int) Math.abs(angleTemp/90);

     for(int i = 0; i != number; ++i){            
         cvTranspose(rotatedImage, rotatedImage);
         cvFlip(rotatedImage, rotatedImage, 0);           
     }

Ends up in throwing exception saying that source and destination doesn't match with number of columns and rows. 最后抛出异常,表明源和目标与列和行的数量不匹配。

Update: 更新:

Video is recorded in this way. 以这种方式录制视频。

IplImage newImage = null;
if(cameraSelection == CameraInfo.CAMERA_FACING_FRONT){
    newImage = videoRecorder.rotate(yuvIplImage, 180);
    videoRecorder.record(newImage);
}
else
    videoRecorder.record(yuvIplImage);  

Rotation is done in this way: 旋转是通过以下方式完成的:

    IplImage img = IplImage.create(image.height(), image.width(),
            image.depth(), image.nChannels());

    for (int i = 0; i < 180; i++) {
        cvTranspose(image, img);
        cvFlip(img, img, 0);
    }

Can anyone point out what is wrong here if you have experienced this before? 如果您以前曾经历过,谁能指出这里有什么问题?

Seeing that you already have an IplImage, you may be able to find this helpful. 看到您已经拥有一个IplImage,您也许可以找到帮助。 I have modified the onPreviewFrame method of this Open Source Android Touch-To-Record library to take transpose and resize a captured frame. 我已经修改了此开源Android Touch-To-Record库的onPreviewFrame方法,以进行转置并调整捕获帧的大小。

I defined "yuvIplImage" as following in my setCameraParams() method. 我在setCameraParams()方法中定义了“ yuvIplImage”。

IplImage yuvIplImage = IplImage.create(mPreviewSize.height, mPreviewSize.width, opencv_core.IPL_DEPTH_8U, 2);

Also initialize video recorder like this, sending width as height and vice versa: 还要像这样初始化录像机,将宽度发送为高度,反之亦然:

//call initVideoRecorder() method like this to initialize videoRecorder object of FFmpegFrameRecorder class.
initVideoRecorder(strVideoPath, mPreview.getPreviewSize().height, mPreview.getPreviewSize().width, recorderParameters);

//method implementation
public void initVideoRecorder(String videoPath, int width, int height, RecorderParameters recorderParameters)
{
    Log.e(TAG, "initVideoRecorder");

    videoRecorder = new FFmpegFrameRecorder(videoPath, width, height, 1);
    videoRecorder.setFormat(recorderParameters.getVideoOutputFormat());
    videoRecorder.setSampleRate(recorderParameters.getAudioSamplingRate());
    videoRecorder.setFrameRate(recorderParameters.getVideoFrameRate());
    videoRecorder.setVideoCodec(recorderParameters.getVideoCodec());
    videoRecorder.setVideoQuality(recorderParameters.getVideoQuality());
    videoRecorder.setAudioQuality(recorderParameters.getVideoQuality());
    videoRecorder.setAudioCodec(recorderParameters.getAudioCodec());
    videoRecorder.setVideoBitrate(1000000);
    videoRecorder.setAudioBitrate(64000);
}

This is my onPreviewFrame() method: 这是我的onPreviewFrame()方法:

@Override
public void onPreviewFrame(byte[] data, Camera camera)
{

    long frameTimeStamp = 0L;

    if(FragmentCamera.mAudioTimestamp == 0L && FragmentCamera.firstTime > 0L)
    {
        frameTimeStamp = 1000L * (System.currentTimeMillis() - FragmentCamera.firstTime);
    }
    else if(FragmentCamera.mLastAudioTimestamp == FragmentCamera.mAudioTimestamp)
    {
        frameTimeStamp = FragmentCamera.mAudioTimestamp + FragmentCamera.frameTime;
    }
    else
    {
        long l2 = (System.nanoTime() - FragmentCamera.mAudioTimeRecorded) / 1000L;
        frameTimeStamp = l2 + FragmentCamera.mAudioTimestamp;
        FragmentCamera.mLastAudioTimestamp = FragmentCamera.mAudioTimestamp;
    }

    synchronized(FragmentCamera.mVideoRecordLock)
    {
        if(FragmentCamera.recording && FragmentCamera.rec && lastSavedframe != null && lastSavedframe.getFrameBytesData() != null && yuvIplImage != null)
        {
            FragmentCamera.mVideoTimestamp += FragmentCamera.frameTime;

            if(lastSavedframe.getTimeStamp() > FragmentCamera.mVideoTimestamp)
            {
                FragmentCamera.mVideoTimestamp = lastSavedframe.getTimeStamp();
            }

            try
            {
                yuvIplImage.getByteBuffer().put(lastSavedframe.getFrameBytesData());

                IplImage bgrImage = IplImage.create(mPreviewSize.width, mPreviewSize.height, opencv_core.IPL_DEPTH_8U, 4);// In my case, mPreviewSize.width = 1280 and mPreviewSize.height = 720
                IplImage transposed = IplImage.create(mPreviewSize.height, mPreviewSize.width, yuvIplImage.depth(), 4);
                IplImage squared = IplImage.create(mPreviewSize.height, mPreviewSize.height, yuvIplImage.depth(), 4);

                int[] _temp = new int[mPreviewSize.width * mPreviewSize.height];

                Util.YUV_NV21_TO_BGR(_temp, data, mPreviewSize.width,  mPreviewSize.height);

                bgrImage.getIntBuffer().put(_temp);

                opencv_core.cvTranspose(bgrImage, transposed);
                opencv_core.cvFlip(transposed, transposed, 1);

                opencv_core.cvSetImageROI(transposed, opencv_core.cvRect(0, 0, mPreviewSize.height, mPreviewSize.height));
                opencv_core.cvCopy(transposed, squared, null);
                opencv_core.cvResetImageROI(transposed);

                videoRecorder.setTimestamp(lastSavedframe.getTimeStamp());
                videoRecorder.record(squared);
            }
            catch(com.googlecode.javacv.FrameRecorder.Exception e)
            {
                e.printStackTrace();
            }
        }

        lastSavedframe = new SavedFrames(data, frameTimeStamp);
    }
}

This code uses a method "YUV_NV21_TO_BGR", which I found from this link 这段代码使用了方法“ YUV_NV21_TO_BGR”,我从此链接中找到了该方法

Basically I had the same problem as yours, "The Green Devil problem on Android". 基本上,我遇到了与您相同的问题,“ Android上的绿色魔鬼问题”。 Before adding "YUV_NV21_TO_BGR" method when I just took transpose of YuvIplImage, more importantly a combination of transpose, flip (with or without resizing), there was greenish output in resulting video, almost like yours. 在我刚开始对YuvIplImage进行转置时添加“ YUV_NV21_TO_BGR”方法之前,更重要的是转置,翻转(有或没有调整大小)的组合,结果视频中的输出都是绿色的,几乎与您的一样。 This "YUV_NV21_TO_BGR" method removed greenish output problem. 此“ YUV_NV21_TO_BGR”方法消除了绿色输出问题。 Thanks to @David Han from above google groups thread. 感谢Google群组线程上方的@David Han。

Also you should know that all this processing (transpose, flip and resize), in onPreviewFrame, takes much time which causes a very serious hit on your Frames Per Second (FPS) rate. 您还应该知道,在onPreviewFrame中进行的所有这些处理(转置,翻转和调整大小)都需要花费大量时间,这会严重打击您的每秒帧数(FPS)速率。 When I used this code, inside onPreviewFrame method, the resulting FPS of the recorded video was down to 3 frames/sec from 30fps. 当我在onPreviewFrame方法中使用此代码时,录制视频的最终FPS从30fps下降到3帧/秒。

When you do the transpose, the images width and height value gets replaced. 进行转置时,图像的宽度和高度值将被替换。 Its like you rotate a rectangle by 90 degrees so that the height becomes width and vice-versa. 就像您将矩形旋转90度一样,使高度变为宽度,反之亦然。 So you need to do some thing like below: 因此,您需要执行以下操作:

    IplImage rotate(IplImage  IplSrc)
{
    IplImage img= IplImage.create(IplSrc.height(),
    IplSrc.width(),
    IplSrc.depth(),
    IplSrc.nChannels());
    cvTranspose(IplSrc, img);
    cvFlip(img, img, 0);
    //cvFlip(img, img, 0);
    return img;

}
    private void ChangeOrientation() throws  com.googlecode.javacv.FrameGrabber.Exception, com.googlecode.javacv.FrameRecorder.Exception {

        //Initialize Frame Grabber
        File f = new File(nativePath);
        frameGrabber = new FFmpegFrameGrabber(f);
        frameGrabber.start();

        Frame captured_frame = null;

        //Initialize Recorder
        initRecorder() ;

        //Loop through the grabber
        boolean inLoop=true;
        while (inLoop) 
        {
            captured_frame = frameGrabber.grabFrame();
            if (captured_frame == null) 
            {
                //break loop
                inLoop=false;
            }
            else if(inLoop)
            {
                // continue looping
                IplSrc=captured_frame.image;

                                  recorder.reocord(rotateImg(IplSrc));
            }
        }
        if (recorder != null ) 
        {
            recorder.stop();
            recorder.release();
            frameGrabber.stop();
            initRecorder=false;
        }
    }


private void initRecorder() throws com.googlecode.javacv.FrameRecorder.Exception 
    {
        recorder = new FFmpegFrameRecorder(editedPath, 
                frameGrabber.getImageWidth(), 
                frameGrabber.getImageHeight(),
                frameGrabber.getAudioChannels());

        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        recorder.setFormat("mp4");
    recorder.setFrameRate(frameGrabber.getFrameRate());
            FrameRate=frameGrabber.getFrameRate();
        }
        recorder.setSampleFormat(frameGrabber.getSampleFormat());
        recorder.setSampleRate(frameGrabber.getSampleRate()); 
        recorder.start();
        initRecorder=true;
    }

This piece of code will help you handle problem when rotate IplImage 这段代码将帮助您处理旋转IplImage时的问题

@Override
    public void onPreviewFrame(byte[] data, Camera camera) {  
         //IplImage newImage = cvCreateImage(cvGetSize(yuvIplimage), IPL_DEPTH_8U, 1);   
         if (recording) {    
             videoTimestamp = 1000 * (System.currentTimeMillis() - startTime);                
             yuvimage = IplImage.create(imageWidth, imageHeight * 3 / 2, IPL_DEPTH_8U,1); 
             yuvimage.getByteBuffer().put(data); 

             rgbimage = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 3); 
             opencv_imgproc.cvCvtColor(yuvimage, rgbimage, opencv_imgproc.CV_YUV2BGR_NV21);      

             IplImage rotateimage=null;
                  try {
                        recorder.setTimestamp(videoTimestamp);   
                        int rot=0;
                        switch (degrees) {
                        case 0:
                            rot =1;
                            rotateimage=rotate(rgbimage,rot);
                        break;
                        case 180:
                            rot = -1;
                            rotateimage=rotate(rgbimage,rot);
                            break;                     
                        default:
                            rotateimage=rgbimage;
                    }                      
                        recorder.record(rotateimage);

                  } catch (FFmpegFrameRecorder.Exception e) {
                     e.printStackTrace();
                  }  
            }

        }
IplImage rotate(IplImage IplSrc,int angle) {
    IplImage img= IplImage.create(IplSrc.height(), IplSrc.width(), IplSrc.depth(), IplSrc.nChannels());
    cvTranspose(IplSrc, img);
    cvFlip(img, img, angle);        
    return img;
    }    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM