Rotate VideoCapture in OpenCV on Android - java

How to rotate the camera when using class VideoCapture on OpenCV? (Sample Face Detection on Android).
I'm rotating the canvas with:
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
Matrix matrix = new Matrix();
matrix.preTranslate(
(canvas.getWidth() - bmp.getWidth()) / 2,
(canvas.getHeight() - bmp.getHeight()) / 2);
matrix.postRotate(270f, (canvas.getWidth()) / 2,
(canvas.getHeight()) / 2);
canvas.drawBitmap(bmp, matrix, null);
}
but image from Camera doesn't rotate: Face Detect dont work.
The camera receives the stream from the following:
protected Bitmap processFrame(VideoCapture capture) {
capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
capture.retrieve(mGray,
Highgui.CV_CAP_ANDROID_GREY_FRAME);
UPDATE
I did the following:
#Override
protected Bitmap processFrame(VideoCapture capture) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
Core.flip(mRgba.t(), mRgba, 0);
}
else {
}
capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
capture.retrieve(mDetect_thread.mGray,
Highgui.CV_CAP_ANDROID_GREY_FRAME);
But is dont work. When I run the program in portret orientation(on android device)- program don't start When i run the rogram in landscape orientation - programm work, but when i rotation the device, program work, but image on display dont rotation

Your question is mostly a duplicate of this, except that you are looking for the Android version. It is quite similar but here it is, 90º rotation can be obtained by transposing and then flipping the image:
# rotate 90º counter-clockwise
Core.flip(mRgba.t(), mRgba, 0); //mRgba.t() is the transpose
# rotate 90º clockwise
Core.flip(mRgba.t(), mRgba, 1);
For other rotations you can use warpAffine:
Point center = new Point(x,y);
double angle = 90;
double scale = 1.0;
Mat mapMatrix = Imgproc.getRotationMatrix2D(center, angle, scale);
Imgproc.warpAffine(srcMat, dstMat, mapMatrix, Imgproc.INTER_LINEAR);
EDIT - more complete examples follow:
/**
* Image is first resized-to-fit the dst Mat and then rotated.
* mRgba is the source image, mIntermediateMat should have the same type.
*/
private void rotationTutorial(){
double ratio = mRgba.height() / (double) mRgba.width();
int rotatedHeight = mRgba.height();
int rotatedWidth = (int) Math.round(mRgba.height() * ratio);
Imgproc.resize(mRgba, mIntermediateMat, new Size(rotatedHeight, rotatedWidth));
Core.flip(mIntermediateMat.t(), mIntermediateMat, 0);
Mat ROI = mRgba.submat(0, mIntermediateMat.rows(), 0, mIntermediateMat.cols());
mIntermediateMat.copyTo(ROI);
}
/**
* Image is rotated - cropped-to-fit dst Mat.
*
*/
private void rotationAffineTutorial(){
// assuming source image's with and height are a pair value:
int centerX = Math.round(mRgba.width()/2);
int centerY = Math.round(mRgba.height()/2);
Point center = new Point(centerY,centerX);
double angle = 90;
double scale = 1.0;
double ratio = mRgba.height() / (double) mRgba.width();
int rotatedHeight = (int) Math.round(mRgba.height());
int rotatedWidth = (int) Math.round(mRgba.height() * ratio);
Mat mapMatrix = Imgproc.getRotationMatrix2D(center, angle, scale);
Size rotatedSize = new Size(rotatedWidth, rotatedHeight);
mIntermediateMat = new Mat(rotatedSize, mRgba.type());
Imgproc.warpAffine(mRgba, mIntermediateMat, mapMatrix, mIntermediateMat.size(), Imgproc.INTER_LINEAR);
Mat ROI = mRgba.submat(0, mIntermediateMat.rows(), 0, mIntermediateMat.cols());
mIntermediateMat.copyTo(ROI);
}
Note:
These examples might be orientation-specific, I made them for landscape orientation.
You should not call the code from these examples for every video frame. Some of the code should only run once.

If you only need to do 90, 180, or 270 degrees rotation (Which seems to be your case) you better use Core.flip() which is faster.
Here below a method that does it for you:
public static Mat rotate(Mat src, double angle)
{
Mat dst = new Mat();
if(angle == 180 || angle == -180) {
Core.flip(src, dst, -1);
} else if(angle == 90 || angle == -270) {
Core.flip(src.t(), dst, 1);
} else if(angle == 270 || angle == -90) {
Core.flip(src.t(), dst, 0);
}
return dst;
}

Related

Rotating an image around a reference point [duplicate]

This question already has answers here:
Rotate a buffered image in Java
(4 answers)
Closed 1 year ago.
I'm trying to rotate the image around a reference point in Java using BufferedImage and AffineTransform, at first it seemed exactly what I needed, but it turns out it doesn't behave as expected. I need to do some rudimentary rotations, in multiples of 90, so I tried to do getQuadrantRotateInstance, but, if the reference point is at 0,0 then I get a RasterFormatException: Transformed height (0) is less than or equal to 0.
var rotation = switch (transform) {
case TRANS_NONE -> 0;
case TRANS_ROT90 -> 1;
case TRANS_ROT180 -> 2;
case TRANS_ROT270 -> 3;
default -> throw new NotImplementedException();
};
var transform = AffineTransform.getQuadrantRotateInstance(rotation, referenceX, referenceY);
var operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
var rotated = operation.filter(source, null);
By the looks of it the image gets rotated out of the canvas (into negative coordinates), resulting in exception above.
What would be the proper solution to create a rotated variant of the image without cropping or rotating around a center point like existing solutions do?
Rotating an image by an angle around the center point:
private BufferedImage rotateImage(BufferedImage buffImage, double angle) {
double radian = Math.toRadians(angle);
double sin = Math.abs(Math.sin(radian));
double cos = Math.abs(Math.cos(radian));
int width = buffImage.getWidth();
int height = buffImage.getHeight();
int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);
BufferedImage rotatedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = rotatedImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
// This is the rotation around the center point - change this line
graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
graphics.drawImage(buffImage, 0, 0, null);
graphics.dispose();
return rotatedImage;
}
To change the origin point of the rotation see javadoc of the method rotate.
Source: Creating simple captcha.

Draw text on bitmap in the bottom left corner

I'm trying to draw some text on bitmap with a fixed position (Bottom left corner) no matter how bitmap size different.
Code below works but, the Text is drawn on the center of the bitmap
public Bitmap drawTextToBitmap(Context gContext,
Bitmap bitmap,
String gText) {
Resources resources = gContext.getResources();
float scale = resources.getDisplayMetrics().density;
android.graphics.Bitmap.Config bitmapConfig =
bitmap.getConfig();
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(getResources().getColor(R.color.fujiColor));
paint.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/DS-DIGI.TTF"));
paint.setTextSize((int) (14 * scale));
paint.setShadowLayer(1f, 0f, 1f, getResources().getColor(R.color.fujiShadowColor));
Rect bounds = new Rect();
paint.getTextBounds(gText, 0, gText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width()) / 2;
int y = (bitmap.getHeight() + bounds.height()) / 2;
canvas.drawText(gText, x, y, paint);
return bitmap;
}
What I need is something similar to this :
Thank you.
As mentioned in the official docs, the text is drawn taking the (x,y) values as origin. Change the x,y values. Something along the following lines should work.
int horizontalSpacing = 24;
int verticalSpacing = 36;
int x = horizontalSpacing;//(bitmap.getWidth() - bounds.width()) / 2;
int y = bitmap.getHeight()-verticalSpacing;//(bitmap.getHeight() + bounds.height()) / 2;

Make video fill screen and keep aspect ratio

Java and C# (Xamarin.Android) accepted
I have a MediaRecorder that streams the cameraframes onto a TextureView (basic camera2 approach)
the TextureView fills my screen.
I want the video to fill the screen but keep the aspect ratio.
I know I have to eventually cut of either left/right or top/bottom parts of the video, to keep the aspect ratio.
How can I make the video fit the TextureView and "zoom in" until the whole screen is filled? I don't want "black bars" that compromise for the ratio to be kept.
I guess this has to be accomplished through overriding the OnMeasure of the TextureView
C# extended TextureView
//Called by my MediaRecorder Implementation, when I set the video dimensions (mostly width = 1280, height = 720)
public void SetAspectRatio(int width, int height)
{
if (width == 0 || height == 0)
throw new ArgumentException("Size cannot be negative.");
mRatioWidth = width;
mRatioHeight = height;
RequestLayout();
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.GetSize(widthMeasureSpec);
int height = MeasureSpec.GetSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight)
{
SetMeasuredDimension(width, height);
}
else
{
if (width > (float)height * mRatioWidth / (float)mRatioHeight)
{
SetMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
}
else
{
SetMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
}
I have found the answer, thanks to this comment from this post.
C# Xamarin.Android:
I call the following method when my surface texture size changes (OnSurfaceSizeTextureChanged callback in SurfaceTextureListener)
public void ConfigureTransform(int viewWidth, int viewHeight)
{
int rotation = (int)ThisActivity.WindowManager.DefaultDisplay.Rotation;
var matrix = new Matrix();
var viewRect = new RectF(0, 0, viewWidth, viewHeight);
var bufferRect = new RectF(0, 0, previewSize.Height, previewSize.Width);
float centerX = viewRect.CenterX();
float centerY = viewRect.CenterY();
//Set the scale accordingly, to have the preview texture view fill the whole screen with cutting of the least amount of the preview as possible
//(but this way we keep the aspect ratio -> no preview distortion)
bool isLandscape = ((int)SurfaceOrientation.Rotation90 == rotation || (int)SurfaceOrientation.Rotation270 == rotation);
float scale;
if (isLandscape)
{
scale = System.Math.Max(
(float)viewWidth / previewSize.Height,
(float)viewWidth / previewSize.Width);
}
else
{
scale = System.Math.Max(
(float)viewHeight / previewSize.Height,
(float)viewHeight / previewSize.Width);
}
if ((int)SurfaceOrientation.Rotation90 == rotation || (int)SurfaceOrientation.Rotation270 == rotation || (int)SurfaceOrientation.Rotation180 == rotation)
{
bufferRect.Offset((centerX - bufferRect.CenterX()), (centerY - bufferRect.CenterY()));
matrix.SetRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.Fill);
matrix.PostScale(scale, scale, centerX, centerY);
//Small check if orientation is Reverse Portrait -> dont substract 2 from rotation, otherwise the preview is flipped by 180°
if ((int)SurfaceOrientation.Rotation180 == rotation)
matrix.PostRotate(90 * rotation, centerX, centerY);
else
matrix.PostRotate(90 * (rotation - 2), centerX, centerY);
}
TextureView.SetTransform(matrix);
}

How can I detect a click on a rotated image in Slick2D?

I have an image that I rotate before I draw. The image is rotated by the angles of a hexagon. In other words, the image basically "highlights" the individual edges of a hexagon. I need to detect if the mouse was clicked inside of this rotated image. Detecting a mouse click inside of an unrotated image is pretty simple, but I have no idea about how to detect clicks within rotated points. Is there a way to get the points of the image's corners after rotation so I can place an invisible polygon on top of the image and use Polygon.contains()?
Image highlightEdge = new Image("assets/img/highlightEdge.png");
if(angle == 90){
highlightEdge.setCenterOfRotation(highlightEdge.getWidth(), 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(testPoint.x - 56, testPoint.y);
} else if(angle == 210) {
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x - 72, lastSettlement.y - 32);
} else if( angle == 330){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x - 8, lastSettlement.y - 32);
} else if(angle == 30){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-8, lastSettlement.y);
} else if(angle == 150){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-72, lastSettlement.y);
} else {
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-40, lastSettlement.y - 48);
}
You could create a Shape to exactly match the shape of the Image, and then use its method contains to detect if the mouse was clicked inside.
To take in consideration the rotation of the Image you could apply a corresponding rotation Transform to the Shape.
I created the method shapeFromImage that does this; it receives an Image and its position and returns the corresponding Shape:
/**
* Returns the Shape of an Image considering its rotation
* #param image
* #param x the x position of the Image
* #param y the y position of the Image
*/
public static Shape shapeFromImage(Image image, float x, float y) {
// create a rectangle with same position and size of the image
Shape imageShape = new Rectangle(x, y, image.getWidth(), image.getHeight());
// get the rotation angle of the image
float angle = image.getRotation();
// if the image is rotated, we also need to rotate our shape
if (angle != 0.f) {
// convert the rotation angle in radians to use in Transform
float angleInRadians = (float) Math.toRadians(angle);
// get the point of rotation to use in Transform.
// image.getCenterOfRotation returns a point relative to the image.
// for Transform we need an absolute point, so we add the image position to it
float rotationX = image.getCenterOfRotationX() + x;
float rotationY = image.getCenterOfRotationY() + y;
// create the rotation Transform to match the image rotation
Transform rotationTransform = Transform.createRotateTransform(angleInRadians, rotationX, rotationY);
// apply the rotation Transform to our shape
imageShape = imageShape.transform(rotationTransform);
}
return imageShape;
}
In your example you could use it like this:
float positionX;
float positionY;
if (angle == 90) {
highlightEdge.setCenterOfRotation(highlightEdge.getWidth(), 0);
highlightEdge.rotate(new Float(angle));
positionX = testPoint.x - 56;
positionY = testPoint.y;
highlightEdge.draw(positionX, positionY);
}
...
// you can now use this Shape to use its method "contains"
imageShape = shapeFromImage(highlightEdge, positionX, positionY);

Trouble rotating image in Android aplication

I am currently writing an android application that should rotate an image towards a set location based on the users current location. I can tell that the set location is setting correctly, and the current location seems to be updating, but instead of rotating the image, the app just zooms in on the image and then does nothing. Any help would really be appreciated!
public double bearing(double lat1, double lon1, double lat2, double lon2) {
double longitude1 = lon1;
double longitude2 = lon2;
double latitude1 = Math.toRadians(lat1);
double latitude2 = Math.toRadians(lat2);
double longDiff = Math.toRadians(longitude2 - longitude1);
double y = Math.sin(longDiff) * Math.cos(latitude2);
double x = Math.cos(latitude1) * Math.sin(latitude2) - Math.sin(latitude1) * Math.cos(latitude2) * Math.cos(longDiff);
return (Math.toDegrees(Math.atan2(y, x)) + 360) % 360;
}
private void rotateImageView(ImageView imageView, int drawable, float rotate) {
// Decode the drawable into a bitmap
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
drawable);
// Get the width/height of the drawable
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = bitmapOrg.getWidth(), height = bitmapOrg.getHeight();
// Initialize a new Matrix
Matrix matrix = new Matrix();
// Decide on how much to rotate
rotate = rotate % 360;
// Actually rotate the image
matrix.postRotate(rotate, width, height);
// recreate the new Bitmap via a couple conditions
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, width, height, matrix, true);
//BitmapDrawable bmd = new BitmapDrawable( rotatedBitmap );
//imageView.setImageBitmap( rotatedBitmap );
imageView.setImageDrawable(new BitmapDrawable(getResources(), rotatedBitmap));
imageView.setScaleType(ImageView.ScaleType.CENTER);
}
public void onLocationChange() {
// If we don't have a Location, we break out
if (currentLocation == null) return;
double azimuth = bearing(currentLocation.getLatitude(), currentLocation.getLongitude(), baseLocation.getLatitude(), baseLocation.getLongitude());
double baseAzimuth = azimuth;
GeomagneticField geoField = new GeomagneticField(Double
.valueOf(currentLocation.getLatitude()).floatValue(), Double
.valueOf(currentLocation.getLongitude()).floatValue(),
Double.valueOf(currentLocation.getAltitude()).floatValue(),
System.currentTimeMillis()
);
azimuth -= geoField.getDeclination(); // converts magnetic north into true north
// Store the bearingTo in the bearTo variable
float bearTo = currentLocation.bearingTo(baseLocation);
// If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
if (bearTo < 0) {
bearTo = bearTo + 360;
}
//This is where we choose to point it
double direction = bearTo - azimuth;
// If the direction is smaller than 0, add 360 to get the rotation clockwise.
if (direction < 0) {
direction = direction + 360;
}
float fDir = (float) direction;
rotateImageView((ImageView) findViewById(R.id.arrow), R.drawable.ic_launcher, fDir);
}
private Runnable updateTimeChange = new Runnable() {
public void run() {
onLocationChange();
customHandler.postDelayed(this, 500);
}
};
If your target is greater than 11 then you can try view.setRotation() method:
image.setRotation(angle); // this is for your imageview where image is the imageview object
From the developers page. getRotation()
Sets the degrees that the view is rotated around the pivot point. Increasing values result in clockwise rotation.
If you want to setRotation in xml the xml tag is :
android:rotation

Categories

Resources