Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does BoofCV constantly rotate the Camera Preview to the left?

I'm trying to use the BoofCV line detection with the given example from the BoofCV Android Demo. For this I copied the classes and set everything up with the Camera API from Android. Although the Demo is using the Landscape Orientation, my Activity needs to be in Portrait, but when set the camera is rotated 90° to the left. When I try to set the Camera accordingly, nothing happens. I used:

  • Camera.setDisplayOrientation(90)
  • Camera.setParameters("orientation", "portrait")

After a while I figured out that it is not device related (tested on different devices and API levels) and it doesn't have anything to do with the Camera API as well (since I managed to get it in portrait when commenting out the VideoProcessor.init() function).

After trying it for a while I still can't figure out why the VideoProcessor keeps rotating the Image to the left...

Here is my code for the VideoProcessor:

public class LineProcessor extends Thread implements VideoProcessing {

    /**
     * Lock for reading and writing images with processing and render
     */
    private final Object lockGui = new Object();

    /**
     * Lock used when converting the video stream.
     */
    private final Object lockConvert = new Object();

    private Paint mPaint;
    private ImageType<GrayU8> imageType;
    private GrayU8 image;
    private GrayU8 image2;
    private volatile boolean requestStop = false;
    private volatile boolean running = false;
    private int outputWidth;
    private int outputHeight;
    private View view;
    private Thread thread;

    private DetectLine detector;
    private FastQueue<LineSegment2D_F32> lines = new FastQueue<LineSegment2D_F32>(LineSegment2D_F32.class,true);
    private Bitmap bitmap;
    private byte[] storage;

    private double scale;
    private double tranX,tranY;

    /**
     * Creates a new Line Processor from a Line Detector
     * @param detector the Line Detector Segment
     */
    public LineProcessor(DetectLine detector) {
        this.imageType = ImageType.single(GrayU8.class);
        this.detector = detector;

        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2.0f);
    }

    @Override
    public void init(View view, Camera camera) {
        synchronized (lockGui) {
            this.view = view;

            Camera.Size size = camera.getParameters().getPreviewSize();
            outputWidth = size.width;
            outputHeight = size.height;
            declareImages(size.width,size.height);
        }

        // start the thread for processing
        running = true;
        start();
    }



    @Override
    public void onDraw(Canvas canvas) {
        synchronized (lockGui) {
            // the process class could have been swapped
            if( image == null )
                return;

            int w = view.getWidth();
            int h = view.getHeight();

            // fill the window and center it
            double scaleX = w/(double)outputWidth;
            double scaleY = h/(double)outputHeight;

            scale = Math.min(scaleX,scaleY);
            tranX = (w-scale*outputWidth)/2;
            tranY = (h-scale*outputHeight)/2;

            canvas.translate((float)tranX,(float)tranY);
            canvas.scale((float)scale,(float)scale);

            render(canvas, scale);
        }
    }



    @Override
    public void convertPreview(byte[] bytes, Camera camera) {
        if( thread == null )
            return;

        synchronized ( lockConvert ) {
            ConvertUtils.nv21ToGray(bytes, image.width, image.height, image);

        }
        // wake up the thread and tell it to do some processing
        thread.interrupt();
    }

    @Override
    public void stopProcessing() {
        if( thread == null )
            return;

        requestStop = true;
        while( running ) {
            // wake the thread up if needed
            thread.interrupt();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {}
        }
    }

    @Override
    public void run() {
        thread = Thread.currentThread();
        while( !requestStop ) {
            synchronized ( thread ) {
                try {
                    wait();
                    if( requestStop )
                        break;
                } catch (InterruptedException e) {}
            }

            // swap gray buffers so that convertPreview is modifying the copy which is not in use
            synchronized ( lockConvert ) {
                GrayU8 tmp = image;
                image = image2;
                image2 = tmp;
            }

            process(image2);

            view.postInvalidate();
        }
        running = false;
    }

    /**
     * Scaling applied to the drawing canvas
     */
    public double getScale() {
        return scale;
    }

    /**
     * Translation x applied to the drawing canvas
     */
    public double getTranX() {
        return tranX;
    }

    /**
     * Translation y applied to the drawing canvas
     */
    public double getTranY() {
        return tranY;
    }


    public void process(GrayU8 gray) {

        if( detector != null ) {
            List<LineParametric2D_F32> found = detector.detect(gray);

            synchronized ( lockGui ) {
                ConvertUtils.grayToBitmap(gray,bitmap,storage);
                lines.reset();
                for( LineParametric2D_F32 p : found ) {
                    LineSegment2D_F32 ls = ConvertUtils.convert(p, gray.width,gray.height);
                    lines.grow().set(ls.a,ls.b);
                }
            }

        }
    }

    protected void render(Canvas canvas, double imageToOutput) {

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

        for( LineSegment2D_F32 s : lines.toList() )  {
            canvas.drawLine(s.a.x,s.a.y,s.b.x,s.b.y,mPaint);
        }

}

    protected void declareImages( int width , int height ) {
        image = imageType.createImage(width, height);
        image2 = imageType.createImage(width, height);

        bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
        storage = ConvertUtils.declareStorage(bitmap,storage);
    }
}

the class I extended from is VideoProcessing.java

Does anybody have any experience with this issue?

like image 658
1resu Avatar asked Oct 30 '22 00:10

1resu


1 Answers

The solution is changing the render function to the following:

    protected void render(Canvas canvas, double imageToOutput) {

        canvas.rotate(90, 640f/2, 480f/2);
        canvas.scale(480f/640f, 640f/480f, 640f/2, 480f/2);
        canvas.drawBitmap(bitmap,0,0,null);

        for( LineSegment2D_F32 s : lines.toList() )  {
            canvas.drawLine(s.a.x,s.a.y,s.b.x,s.b.y,mPaint);
        }
}

I thought this wasn't the clean way before, but it's actually the only working way....

like image 134
1resu Avatar answered Nov 15 '22 07:11

1resu