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?
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....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With