I have been searching the internet for a while now. The problem is that the solution to my problem in mostly available in either python or C++. I have tried to replicate the code but no luck.
I have detected a card (Rectangle) and I am able to crop it if the rectangle is straight but if the rectangle is rotated at an angle I get a image that cuts the card.
Image showing what I want to achieve...
My working code for straight Image.
Bitmap abc = null;
Point topleft, topright, bottomleft, bottomright;
float xRatio = (float) original.getWidth() / sourceImageView.getWidth();
float yRatio = (float) original.getHeight() / sourceImageView.getHeight();
float x1 = (points.get(0).x) * xRatio;
float x2 = (points.get(1).x) * xRatio;
float x3 = (points.get(2).x) * xRatio;
float x4 = (points.get(3).x) * xRatio;
float y1 = (points.get(0).y) * yRatio;
float y2 = (points.get(1).y) * yRatio;
float y3 = (points.get(2).y) * yRatio;
float y4 = (points.get(3).y) * yRatio;
Point p1 = new Point(x1, y1);
Point p2 = new Point(x2, y2);
Point p3 = new Point(x3, y3);
Point p4 = new Point(x4, y4);
List<Point> newpoints = new ArrayList<Point>();
newpoints.add(p1);
newpoints.add(p2);
newpoints.add(p3);
newpoints.add(p4);
Collections.sort(newpoints, new Comparator<Point>() {
public int compare(Point o1, Point o2) {
return Double.compare(o1.x, o2.x);
}
});
if (newpoints.get(0).y > newpoints.get(1).y) {
bottomleft = newpoints.get(0);
topleft = newpoints.get(1);
} else {
bottomleft = newpoints.get(1);
topleft = newpoints.get(0);
}
if (newpoints.get(2).y > newpoints.get(3).y) {
bottomright = newpoints.get(2);
topright = newpoints.get(3);
} else {
bottomright = newpoints.get(3);
topright = newpoints.get(2);
}
final Mat newimage = new Mat();
Bitmap bmp32 = original.copy(Bitmap.Config.ARGB_8888, true);
org.opencv.android.Utils.bitmapToMat(bmp32, newimage);
final float dd = getAngle(bottomleft, bottomright);
Mat finalMat = new Mat(newimage, new org.opencv.core.Rect(topleft, bottomright));
abc = RotateBitmap(createBitmapfromMat(finalMat), (-dd));
Current code when rectangle is straight :
Current code when rectangle is rotated:
Links to similar questions :
Link 1 Link 2
Related
I'm trying to make a simple image editor. At the beginning I've thought that it'll be a good idea to simply save view state as Bitmap but, as it turned out, there is a wide range of screen resolutions and that leads to huge quality (and memory usage) fluctuations.
Now I'm trying to make a module that renders views state translated to desired resolution.
In the code below I'm trying to recreate current state of the views in canvas:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.test_1_1);
bitmap = Bitmap.createScaledBitmap(bitmap, parentView.getMeasuredWidth(), parentView.getMeasuredHeight(), true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
for (View rootView : addedViews) {
ImageView imageView = rootView.findViewById(R.id.sticker);
float[] viewPosition = new float[2];
transformToAncestor(viewPosition, parentView, imageView);
Bitmap originalBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Matrix adjustMatrix = new Matrix();
adjustMatrix.postTranslate(viewPosition[0], viewPosition[1]);
adjustMatrix.postScale(
rootView.getScaleX(),
rootView.getScaleY(),
rootView.getWidth() / 2,
rootView.getHeight() / 2);
adjustMatrix.postRotate(rootView.getRotation(),
rootView.getWidth() / 2,
rootView.getHeight() / 2);
canvas.drawBitmap(originalBitmap, adjustMatrix, paint);
}
transformToAncestor function is from here.
public static void transformToAncestor(float[] point, final View ancestor, final View descendant) {
final float scrollX = descendant.getScrollX();
final float scrollY = descendant.getScrollY();
final float left = descendant.getLeft();
final float top = descendant.getTop();
final float px = descendant.getPivotX();
final float py = descendant.getPivotY();
final float tx = descendant.getTranslationX();
final float ty = descendant.getTranslationY();
final float sx = descendant.getScaleX();
final float sy = descendant.getScaleY();
point[0] = left + px + (point[0] - px) * sx + tx - scrollX;
point[1] = top + py + (point[1] - py) * sy + ty - scrollY;
ViewParent parent = descendant.getParent();
if (descendant != ancestor && parent != ancestor && parent instanceof View) {
transformToAncestor(point, ancestor, (View) parent);
}
}
(author wrote a note that his function does not support rotation, but there's not much rotation in my example so I don't think that important for now).
My problem is:
First image is generated via saving the parent view state. Second one is generated by translating views position, rotation and scale onto canvas.
As you can see, on the canvas, not scaled stickers are positioned properly, but scaled are incorrectly positioned.
How to position those scaled views properly?
I've managed to fix the issue myself.
It turned out my solution was nearly OK but I did not took into consideration that my manipulation of a matrix does change the arrangement of the original points, so my
rootView.getWidth() / 2,
rootView.getHeight() / 2
is no longer applicable as a center of the view after calling Matrix.postScale or Matrix.postRotation.
I wanted to:
apply scale with pivot on top left corner,
apply rotation with pivot on the center of the view.
Given the assumptions, here's the working code:
// setup variables for sizing and transformation
float position[] = new float[2];
transformToAncestor(position, rootView, imageView);
float desiredRotation = imageView.getRotation();
float sizeDeltaX = imageView.getMeasuredWidth() / (float) imageBitmap.getWidth();
float sizeDeltaY = imageView.getMeasuredHeight() / (float) imageBitmap.getHeight();
float desiredScaleX = imageView.getScaleX() * sizeDeltaX * scaleX;
float desiredScaleY = imageView.getScaleY() * sizeDeltaY * scaleY;
float imageViewWidth = imageView.getMeasuredWidth() * imageView.getScaleX();
float imageViewHeight = imageView.getMeasuredHeight() * imageView.getScaleY();
float rootViewWidth = rootView.getMeasuredWidth();
float rootViewHeight = rootView.getMeasuredHeight();
float percentXPos = position[0] / rootViewWidth;
float percentYPos = position[1] / rootViewHeight;
float percentXCenterPos = (position[0] + imageViewWidth/2)
/ rootViewWidth;
float percentYCenterPos = (position[1] + imageViewHeight/2)
/ rootViewHeight;
float desiredPositionX = background.getWidth() * percentXPos;
float desiredPositionY = background.getHeight() * percentYPos;
float desiredCenterX = background.getWidth() * percentXCenterPos;
float desiredCenterY = background.getHeight() * percentYCenterPos;
// apply above variables to matrix
Matrix matrix = new Matrix();
float[] points = new float[2];
matrix.postTranslate(
desiredPositionX,
desiredPositionY);
matrix.mapPoints(points);
matrix.postScale(
desiredScaleX,
desiredScaleY,
points[0],
points[1]);
matrix.postRotate(
desiredRotation,
desiredCenterX,
desiredCenterY);
// apply matrix to bitmap, then draw it on canvas
canvas.drawBitmap(imageBitmap, matrix, paint);
As you can see, the mapPoints method was the answer for my question - it simply returns points after tranformation.
How can I draw Path with fading (opacity or thicknes) line? Something like this.
I know there is LinearGradient shader for Paint, but it won't bend along the Path.
One possible solution might be to get points along the Path and just draw it by myself through the segments`. But I coouldn't find any method for that either.
I came up with the following code. The mos important thing is PathMeasure's getPosTan() method.
if (getGesturePath() != null) {
final short steps = 150;
final byte stepDistance = 5;
final byte maxTrailRadius = 15;
pathMeasure.setPath(getGesturePath(), false);
final float pathLength = pathMeasure.getLength();
for (short i = 1; i <= steps; i++) {
final float distance = pathLength - i * stepDistance;
if (distance >= 0) {
final float trailRadius = maxTrailRadius * (1 - (float) i / steps);
pathMeasure.getPosTan(distance, pathPos, null);
final float x = pathPos[0] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
final float y = pathPos[1] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
paint.setShader(new RadialGradient(
x,
y,
trailRadius > 0 ? trailRadius : Float.MIN_VALUE,
ColorUtils.setAlphaComponent(Color.GREEN, random.nextInt(0xff)),
Color.TRANSPARENT,
Shader.TileMode.CLAMP
));
canvas.drawCircle(x, y, trailRadius, paint);
}
}
}
I'm developing ad app withthe GearVR framework in which I must show a sphere and a series of interactive points on this sphere around the camera (a 360 photo with some points on the border). I could create the sphere witha a 360 photo and put the camera in but I can't figure out how to put the points onto the sphere because they are given to me related to the photo (photo is, like, 4608 x 2304 and my point is on the image 280 x 1115). How can I transalate the x,y coordinates to the sphere?
I tried many formulas but none seem to work. Here's my code, thanks in advance:
private int wSphere = 4608;
private int hSphere = 2304;
#Override
public void onInit(GVRContext gvrContext) {
GVRScene scene = gvrContext.getNextMainScene();
GVRSphereSceneObject sphereObject = null;
Future<GVRTexture> texture = gvrContext.loadFutureTexture(new GVRAndroidResource(gvrContext, R.raw.office));
sphereObject = new GVRSphereSceneObject(gvrContext, false, texture);
sphereObject.getTransform().setScale(50f, 50f, 50f);
Future<GVRTexture> futureTexture = gvrContext.loadFutureTexture(new GVRAndroidResource(gvrContext, R.raw.texture));
GVRMaterial material = new GVRMaterial(gvrContext);
material.setMainTexture(futureTexture);
float normX = (280f);
float normY = (1115f);
HashMap<String, Float> positions = xyToXYZ(normX, normY, 50);
GVRCubeSceneObject cubeObject = new GVRCubeSceneObject(gvrContext, true, material);
cubeObject.getTransform().setScale(5.5f, 5.5f, 5.5f);
cubeObject.getTransform().setPosition(positions.get("x"), positions.get("z"), positions.get("y"));
cubeObject.getRenderData().setMaterial(material);
scene.addSceneObject(sphereObject);
scene.addSceneObject(cubeObject);
}
public HashMap<String, Float> xyToXYZ(float x, float y, float r) {
HashMap<String, Float> map = new HashMap<String, Float>();
float theta = (float) (-2* Math.atan((Math.sqrt(Math.pow(x,2) + Math.pow(y,2)))/(2*r)) + 90);
float phi = (float) Math.atan((x/(-y)));
float sinTheta = (float) Math.sin(theta);
float cosTheta = (float) Math.cos(theta);
float sinPhi = (float) Math.sin(phi);
float cosPhi = (float) Math.cos(phi);
float nX = (float) (cosTheta * sinPhi);
float nY = (float) cosPhi * cosTheta;
float nZ = (float) sinTheta;
map.put("x", nX);
map.put("y", nY);
map.put("z", nZ);
return map;
}
I have trouble of finding the hitpoint on the 3d space. I currently have viewport data which is the screen size, the depth from camera to the object (object is = plane parallel with the screen), and the mouse pointer on the screen.
I am using opengl es 2.0, how do if ind the hit point on the object which was parallel with the camera.
Setup for my camera:
App app = App.getInstance();
app.setWidth(width);
app.setHeight(height);
float widthDP = width;
float heightDP = height;
float ratio = widthDP / heightDP ;
mShader = new SimpleShader();
Polygon poly = Polygon.Builder()
.addVertex(new Point(widthDP*6,heightDP*2))
.addVertex(new Point(widthDP*6, 0))
.addVertex(new Point(0, heightDP*2))
.addVertex(new Point(0, 0)).build();
Environment environment = Environment.getInstance();
environment.setPolgyon(poly);
environment.setScreenSize((int)widthDP, (int)heightDP);
Camera camera = Camera.getInstance();
camera.setShader(mShader);
camera.setEye(new Point3D(widthDP*2,heightDP, -heightDP));
camera.setLook(new Point3D(widthDP*2,heightDP,0));
camera.setUp(new Point3D(0,1,0));
environment.setCamera(camera);
Matrix.frustumM(camera.getProjectionMatrix(), 0, -ratio, ratio, -1, 1, 1, 1000);
This is the code I followed from the link: http://schabby.de/picking-opengl-ray-tracing/
App app = App.getInstance();
Vector3D lookAt = new Vector3D(look.x, look.y, look.z);
Vector3D position = new Vector3D(eye.x, eye.y, eye.z);
Vector3D up = new Vector3D(this.up.x, this.up.y, this.up.z);
Vector3D view = Vector3D.sub(lookAt, position);
view.normalize();
Vector3D h = view.cross(up);
h.normalize();
Vector3D v = h.cross(view);
v.normalize();
float rad = (float) (45.0f * Math.PI / 180.0f);
float vLength = (float) Math.tan( rad / 2 ) * 1;
float hLength = vLength * (app.getWidth() / app.getHeight());
v.mult(vLength);
h.mult(hLength);
//point.x = app.getWidth() - point.x;
//point.y = app.getHeight() - point.y;
point.x += app.getWidth() / 2;
point.y += app.getHeight() / 2;
//point.x *= -1;
//point.y *= -1;
Log.w("point", "Width x="+app.getWidth()+" y="+app.getHeight());
point.x /= app.getWidth() / 2;
point.y /= app.getHeight() / 2;
view.mult(1);
h.mult(point.x);
v.mult(point.y);
view.add(h);
view.add(v);
Vector3D pos = Vector3D.add(position, view);
Vector3D dir = Vector3D.sub(pos, position);
float s = -1*pos.z / dir.z;
float[] newPoint = new float[2];
newPoint[0] = pos.x + dir.x *s;
newPoint[1] = pos.y + dir.y *s;
return new Point(newPoint[0], newPoint[1]);
From the image you can see that the ball fired on the left that fire behind it, does not match the calculated trajectory. Im drawing the ball trajectory using an equation from a SO question, this is modified to take into consideration the box2d steps of 30 frames per second. This does calculate a valid trajectory but it does not match the actual trajectory of the ball, the ball has a smaller trajectory. I am applying a box2d force to the ball, this also has a density set and a shape. The shape radius varies depending on the type of ball. Im setting the start velocity in the touchdown event.
public class ProjectileEquation {
public float gravity;
public Vector2 startVelocity = new Vector2();
public Vector2 startPoint = new Vector2();
public Vector2 gravityVec = new Vector2(0,-10f);
public float getX(float n) {
return startVelocity.x * (n * 1/30f) + startPoint.x;
}
public float getY(float n) {
float t = 1/30f * n;
return 0.5f * gravity * t * t + startVelocity.y * t + startPoint.y;
}
}
#Override
public void draw(SpriteBatch batch, float parentAlpha) {
float t = 0f;
float width = this.getWidth();
float height = this.getHeight();
float timeSeparation = this.timeSeparation;
for (int i = 0; i < trajectoryPointCount; i+=timeSeparation) {
//projectileEquation.getTrajectoryPoint(this.getX(), this.getY(), i);
float x = this.getX() + projectileEquation.getX(i);
float y = this.getY() + projectileEquation.getY(i);
batch.setColor(this.getColor());
if(trajectorySprite != null) batch.draw(trajectorySprite, x, y, width, height);
// t += timeSeparation;
}
}
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
if(button==1 || world.showingDialog)return false;
touchPos.set(x, y);
float angle = touchPos.sub(playerCannon.position).angle();
if(angle > 270 ) {
angle = 0;
}
else if(angle >70) {
angle = 70;
}
playerCannon.setAngle(angle);
world.trajPath.controller.angle = angle;
float radians = (float) angle * MathUtils.degreesToRadians;
float ballSpeed = touchPos.sub(playerCannon.position).len()*12;
world.trajPath.projectileEquation.startVelocity.x = (float) (Math.cos(radians) * ballSpeed);
world.trajPath.projectileEquation.startVelocity.y = (float) (Math.sin(radians) * ballSpeed);
return true;
}
public CannonBall(float x, float y, float width, float height, float damage, World world, Cannon cannonOwner) {
super(x, y, width, height, damage, world);
active = false;
shape = new CircleShape();
shape.setRadius(width/2);
FixtureDef fd = new FixtureDef();
fd.shape = shape;
fd.density = 4.5f;
if(cannonOwner.isEnemy) { //Enemy cannon balls cannot hit other enemy cannons just the player
fd.filter.groupIndex = -16;
}
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(this.position);
body = world.createBody(bodyDef);
body.createFixture(fd);
body.setUserData(this);
body.setBullet(true);
this.cannonOwner = cannonOwner;
this.hitByBall = null;
this.particleEffect = null;
}
private CannonBall createCannonBall(float radians, float ballSpeed, float radius, float damage)
{
CannonBall cannonBall = new CannonBall(CannonEnd().x, CannonEnd().y, radius * ballSizeMultiplier, radius * ballSizeMultiplier, damage, this.world, this);
cannonBall.velocity.x = (float) (Math.cos(radians) * ballSpeed);
//cannonBall.velocity.x = (float) ((Math.sqrt(10) * Math.sqrt(29) *
// Math.sqrt((Math.tan(cannon.angle)*Math.tan(cannon.angle))+1)) / Math.sqrt(2 * Math.tan(cannon.angle) - (2 * 10 * 2)/29))* -1f;
cannonBall.velocity.y = (float) (Math.sin(radians) * ballSpeed);
cannonBall.active = true;
//cannonBall.body.applyLinearImpulse(cannonBall.velocity, cannonBall.position);
cannonBall.body.applyForce(cannonBall.velocity, cannonBall.position );
return cannonBall;
}
trajPath = new TrajectoryActor(-10f);
trajPath.setX(playerCannon.CannonEnd().x);
trajPath.setY(playerCannon.CannonEnd().y);
trajPath.setWidth(10f);
trajPath.setHeight(10f);
stage.addActor(trajPath);
Here is a code that I used for one of my other games, which proved to be very precise. The trick is to apply the impulse on the body and read the initial velocity. Having that I calculate 10 positions where the body will be within 0.5 seconds. The language is called Squirrel which is Lua based with C/C++ like syntax. You should be able to grasp what is going on there. What returns from the getTrajectoryPointsForObjectAtImpulse is an array of 10 positions through which the ball will pass within 0.5 seconds.
const TIMESTER_DIVIDOR = 60.0;
function getTrajectoryPoint( startingPosition, startingVelocity, n )
{
local gravity = box2DWorld.GetGravity();
local t = 1 / 60.0;
local stepVelocity = b2Vec2.Create( t * startingVelocity.x, t * startingVelocity.y );
local stepGravity = b2Vec2.Create( t * t * gravity.x, t * t * gravity.y );
local result = b2Vec2.Create( 0, 0 );
result.x = ( startingPosition.x + n * stepVelocity.x + 0.5 * ( n * n + n ) * stepGravity.x ) * MTP;
result.y = ( startingPosition.y + n * stepVelocity.y + 0.5 * ( n * n + n ) * stepGravity.y ) * -MTP;
return result;
}
function getTrajectoryPointsForObjectAtImpulse (object, impulse)
{
if( !object || !impulse ) return [];
local result = [];
object.bBody.ApplyLinearImpulse( impulse, object.bBody.GetWorldCenter() );
local initialVelocity = object.bBody.GetLinearVelocity();
object.bBody.SetLinearVelocity( b2Vec2.Create(0, 0) );
object.bBody.SetActive(false);
for ( local i = 0.0 ; i < ( 0.5 * TIMESTER_DIVIDOR ) ; )
{
result.append( getTrajectoryPoint(object.bBody.GetPosition(), initialVelocity, i.tointeger() ) );
i += ( (0.5 * TIMESTER_DIVIDOR) * 0.1 );
}
return result;
}
If you do not understand any part of the code, please let me know and I will try to explain.