【问题标题】:Issue with image rotation using JavaCV library使用 JavaCV 库的图像旋转问题
【发布时间】:2014-07-03 11:47:48
【问题描述】:

我正在编写一个 Android 应用程序,它可以在指定的时间内录制视频。如果我使用智能手机的后置摄像头录制,一切正常。该应用程序具有暂停/记录功能,如 Vine 应用程序。使用设备的前置摄像头录制时会出现问题。存储/播放视频时,视频表面框架看起来很好,视频是倒置的。到处都有很多关于这个问题的讨论。但我没有找到任何可行的解决方案。

看看下面提到的代码和图像。

这是从前置摄像头拍摄的原始图像。为了看得更清楚,我把它倒过来了。

这是我在旋转后实际得到的:

方法:

     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);

我试过了

     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);           
     }

最终抛出异常,说源和目标与列数和行数不匹配。

更新:

视频就是这样录制的。

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

旋转是这样完成的:

    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);
    }

如果您以前遇到过这种情况,谁能指出这里出了什么问题?

【问题讨论】:

    标签: java android opencv ffmpeg javacv


    【解决方案1】:

    看到您已经有一个 IplImage,您可能会发现这很有帮助。我已经修改了这个Open Source Android Touch-To-Record library 的 onPreviewFrame 方法来转置并调整捕获的帧的大小。

    我在 setCameraParams() 方法中定义了“yuvIplImage”,如下所示。

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

    也像这样初始化录像机,发送宽度作为高度,反之亦然:

    //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);
    }
    

    这是我的 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);
        }
    }
    

    这段代码使用了一个方法“YUV_NV21_TO_BGR”,我从这个link找到的

    基本上我遇到了和你一样的问题,“Android 上的绿魔问题”。在我刚刚对 YuvIplImage 进行转置时添加“YUV_NV21_TO_BGR”方法之前,更重要的是转置、翻转(有或没有调整大小)的组合,结果视频中有绿色输出,几乎和你的一样。这种“YUV_NV21_TO_BGR”方法消除了绿色输出问题。感谢来自上述谷歌群组线程的@David Han。

    您还应该知道,onPreviewFrame 中的所有这些处理(转置、翻转和调整大小)都需要很长时间,这会对您的每秒帧数 (FPS) 速率造成非常严重的影响。当我在 onPreviewFrame 方法中使用这段代码时,录制视频的 FPS 从 30fps 下降到 3 帧/秒。

    【讨论】:

    • 谢谢老兄。我会试试的。
    【解决方案2】:

    当您进行转置时,图像的宽度和高度值会被替换。就像您将矩形旋转 90 度,使高度变为宽度,反之亦然。所以你需要做一些如下的事情:

        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;
    
    }
    

    【讨论】:

    • 我得到了同样的绿色输出。
    • 我正在以 480 x 480 分辨率录制视频。这是一个原因吗?
    • 我认为原因与方向无关。如果你直接记录iplImage,而不使用这个函数呢?我认为这个函数的输入是问题所在。可能是您使用了带有错误参数的 cvColor()。
    • 如果我不使用这个,视频是完美的,但它是颠倒的。
    • 使用这一行:recorder.record(rotate(IplSrc));而不是:recorder.record(IplSrc);无法将颜色变为绿色。你能把你的代码贴在这里吗?
    【解决方案3】:
        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;
        }
    

    【讨论】:

      【解决方案4】:

      这段代码将帮助您处理旋转 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;
          }    
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多