Problem: I have limited the maximum and minimum zoom, everything is OK, but:
If I try to zoom less than the minimum, then to zoom+, it is necessary to do the same amount of motion in the opposite direction. The same is with zoom+. And on the technical side, it looks like this:
values[Matrix.MSCALE_X] changes, despite the fact that before this is:
m.reset();
m.setScale(sx1, sx1);
where sx1 is a FIXED value. Here is my code:
public boolean onScale(ScaleGestureDetector detector) {
try{
saved_gest_scale = newScale;
newScale *= detector.getScaleFactor();
scaleFactor = newScale / oldScale;
target_img_width *= 1/scaleFactor;
if (target_img_width > width)//limit max zoom
{
//fit on screen:
m = new Matrix();
m.reset();
float w_coef = img_width / width;
float sx1 = 1/w_coef;
m.setScale(sx1, sx1);
///========
//apply new size
float sx2 = target_img_width/width;
sx2 = 1/sx2;
m.setScale(sx2, sx2);
float[] values = new float[9];
m.getValues(values);
//center image:
float globalX = values[Matrix.MTRANS_X];
float globalY = values[Matrix.MTRANS_Y];
float wid = values[Matrix.MSCALE_X]*img_width;
float heig = values[Matrix.MSCALE_Y]*img_height;
m.postTranslate(width/2 - wid/2, height/2-heig/2);
if (wid <= width)//limit min-zoom
{
newScale = sx1;
m = new Matrix();
m.reset();
m.setScale(sx1, sx1);
wid = sx1*img_width;
heig = sx1*img_height;
debug.setText(wid + "<width" + "mx=" + values[Matrix.MSCALE_X] );
m.postTranslate(width/2 - wid/2, height/2-heig/2);
}
iw.setImageMatrix(m);
}
oldScale = newScale;
}catch (Exception xx)
{
debug.setText("detector "+ xx.toString());
}
return true;
}
Please help.
Use one parameter scale wich is changed in the onScale method. Also use two constants minScale and maxScale. In the onScale method check if new scale fits in the acceptable interval, and if it does - redraw the image with new scale.
Right now your code is in mess. Somehow limit min-zoom check is located inside limit max zoom, but scaling methods are spread all around. Thus it's very unclean when a new scale becomes current and why it is working wrong.
Related
I had a quick question, and wondered if anyone had any ideas or libraries I could use for this. I am making a java game, and need to make 2d images concave. The problem is, 1: I don't know how to make an image concave. 2: I need the concave effect to be somewhat of a post process, think Oculus Rift. Everything is normal, but the camera of the player distorts the normal 2d images to look 3d. I am a Sophmore, so I don't know very complex math to accomplish this.
Thanks,
-Blue
If you're not using any 3D libraries or anything like that, just implement it as a simple 2D distortion. It doesn't have to be 100% mathematically correct as long as it looks OK. You can create a couple of arrays to store the distorted texture co-ordinates for your bitmap, which means you can pre-calculate the distortion once (which will be slow but only happens once) and then render multiple times using the pre-calculated values (which will be faster).
Here's a simple function using a power formula to generate a distortion field. There's nothing 3D about it, it just sucks in the center of the image to give a concave look:
int distortionU[][];
int distortionV[][];
public void computeDistortion(int width, int height)
{
// this will be really slow but you only have to call it once:
int halfWidth = width / 2;
int halfHeight = height / 2;
// work out the distance from the center in the corners:
double maxDistance = Math.sqrt((double)((halfWidth * halfWidth) + (halfHeight * halfHeight)));
// allocate arrays to store the distorted co-ordinates:
distortionU = new int[width][height];
distortionV = new int[width][height];
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// work out the distortion at this pixel:
// find distance from the center:
int xDiff = x - halfWidth;
int yDiff = y - halfHeight;
double distance = Math.sqrt((double)((xDiff * xDiff) + (yDiff * yDiff)));
// distort the distance using a power function
double invDistance = 1.0 - (distance / maxDistance);
double distortedDistance = (1.0 - Math.pow(invDistance, 1.7)) * maxDistance;
distortedDistance *= 0.7; // zoom in a little bit to avoid gaps at the edges
// work out how much to multiply xDiff and yDiff by:
double distortionFactor = distortedDistance / distance;
xDiff = (int)((double)xDiff * distortionFactor);
yDiff = (int)((double)yDiff * distortionFactor);
// save the distorted co-ordinates
distortionU[x][y] = halfWidth + xDiff;
distortionV[x][y] = halfHeight + yDiff;
// clamp
if(distortionU[x][y] < 0)
distortionU[x][y] = 0;
if(distortionU[x][y] >= width)
distortionU[x][y] = width - 1;
if(distortionV[x][y] < 0)
distortionV[x][y] = 0;
if(distortionV[x][y] >= height)
distortionV[x][y] = height - 1;
}
}
}
Call it once passing the size of the bitmap that you want to distort. You can play around with the values or use a totally different formula to get the effect you want. Using an exponent less than one for the pow() function should give the image a convex look.
Then when you render your bitmap, or copy it to another bitmap, use the values in distortionU and distortionV to distort your bitmap, e.g.:
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// int pixelColor = bitmap.getPixel(x, y); // gets undistorted value
int pixelColor = bitmap.getPixel(distortionU[x][y], distortionV[x][y]); // gets distorted value
canvas.drawPixel(x + offsetX, y + offsetY, pixelColor);
}
}
I don't know what your actual function for drawing a pixel to the canvas is called, the above is just pseudo-code.
I'm trying to retrieve the real coordinates of an image, after the user has drawn a rectangle on a canvas over the image. For this I've used a LayerDrawable, with the bitmap image as the bottom layer. Unfortunately my solution works only, when the scale factor is 1 (no scaling has been performed). If the user zoomed and panned around a little bit with the current solution I get close to the coordinates, but something is amiss and they aren't accurate. Because I can't post the whole code here I've uploaded it to pastebin (link). I also do a little bit of preprocessing and scale the image exactly so it fits the available size of my extended ImageView. For that i use a ViewTreeObserver and on the predraw method I find out exactly how much space i have available and scale the image to that size, so it fits the bigger dimension and the aspect ratio is kept. The code for it is here (link).
The most important parts for this are:
private void fixCoordinates(){
//get utmost left,right,top,bottom corners from both begin and end coordinate
float left = Math.min(beginCoordinate.x, endCoordinate.x);
float top = Math.min(beginCoordinate.y, endCoordinate.y);
float right = Math.max(beginCoordinate.x, endCoordinate.x);
float bottom = Math.max(beginCoordinate.y, endCoordinate.y);
//reassign them to proper begin and end
PointF b = new PointF(left,top);
PointF e = new PointF(right,bottom);
//m[5] and m[2] denote offsets (empty spaces) when they are positive
if(m[2] > 0){
b.x = b.x - m[2];
e.x = e.x - m[2];
}
if(m[5] > 0){
b.y = b.y - m[5];
e.y = e.y - m[5];
}
//safety
if(b.x < 0){
b.x = 0;
}
if(b.y < 0){
b.y = 0;
}
if(e.x > layers[0].getIntrinsicWidth()){
e.x = layers[0].getIntrinsicWidth();
}
if(e.y > layers[0].getIntrinsicHeight()){
e.y = layers[0].getIntrinsicHeight();
}
//we only have one scale factor, because in the preprocessing we rescale and fit the image
setBeginCoordinate(b);
setEndCoordinate(e);
}
after the MotionEvent I use this function to set begin coordinates to be utmost top and left and end coordinates to be bottom and right.
the actual mapping to the original image is done here:
private PointF mapBeginCoordinates(PointF beginCoordinate, PointF endCoordinate){
float left = Math.min(beginCoordinate.x, endCoordinate.x);
float top = Math.min(beginCoordinate.y, endCoordinate.y);
double wAr = UtilFunctions.getAspectRatio(getOriginalWidth(), layers[0].getIntrinsicWidth());
double hAr = UtilFunctions.getAspectRatio(getOriginalHeight(), layers[0].getIntrinsicHeight());
left = (float)((double)left/wAr);
top = (float)((double)top/hAr);
float[] imageMatrix = new float[9];
getImageMatrix().getValues(imageMatrix);
float scaleFactorX = imageMatrix[Matrix.MSCALE_X];
float scaleFactorY = imageMatrix[Matrix.MSCALE_Y];
float fixedTransX = 1;
float fixedTransY = 1;
//m[5] height m[2] width
if(m[2] < 0){
fixedTransX = (m[2]*scaleFactorX);
}
if(m[5] < 0){
fixedTransY = (m[5]*scaleFactorY);
}
left = left/scaleFactorX + Math.abs(fixedTransX);
top = top/scaleFactorY + Math.abs(fixedTransY);
return new PointF(left,top);
}
the code is the same for the end coordinates. The code in pastebin is a bit messy, because I've been trying many different things for 2 days now to get it to work, yet something eludes me. I'll appreciate any help.
I managed to solve my issue by using the Mike Ortiz TouchImageView
the code I used to map the coordinates to the original bitmap is this:
private PointF mapBeginCoordinates(PointF beginCoordinate, PointF endCoordinate) {
//TODO we only have one aspect ratio for the current picture, so we should remove redundancy
float left = Math.min(beginCoordinate.x, endCoordinate.x);
float top = Math.min(beginCoordinate.y, endCoordinate.y);
double wAr = UtilFunctions.getAspectRatio(originalWidth, layers[0].getIntrinsicWidth());
double hAr = UtilFunctions.getAspectRatio(originalHeight, layers[0].getIntrinsicHeight());
if(!isZoomed()) {
left = (float) (left / wAr);
top = (float) (top / hAr);
}
if(isZoomed()) {
PointF b = transformCoordTouchToBitmap(left,top,true);
left = (float) (b.x / wAr);
top = (float) (b.y / hAr);
}
return new PointF(left,top);
}
i still use the fixcoordinates code from above to prepare my left/top - right/bottom positions. Thanks for the answers
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
I am trying to detect a circular shape from an image which appears to have very good definition. I do realize that part of the circle is missing but from what I've read about the Hough transform it doesn't seem like that should cause the problem I'm experiencing.
Input:
Output:
Code:
// Read the image
Mat src = Highgui.imread("input.png");
// Convert it to gray
Mat src_gray = new Mat();
Imgproc.cvtColor(src, src_gray, Imgproc.COLOR_BGR2GRAY);
// Reduce the noise so we avoid false circle detection
//Imgproc.GaussianBlur( src_gray, src_gray, new Size(9, 9), 2, 2 );
Mat circles = new Mat();
/// Apply the Hough Transform to find the circles
Imgproc.HoughCircles(src_gray, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 1, 160, 25, 0, 0);
// Draw the circles detected
for( int i = 0; i < circles.cols(); i++ ) {
double[] vCircle = circles.get(0, i);
Point center = new Point(vCircle[0], vCircle[1]);
int radius = (int) Math.round(vCircle[2]);
// circle center
Core.circle(src, center, 3, new Scalar(0, 255, 0), -1, 8, 0);
// circle outline
Core.circle(src, center, radius, new Scalar(0, 0, 255), 3, 8, 0);
}
// Save the visualized detection.
String filename = "output.png";
System.out.println(String.format("Writing %s", filename));
Highgui.imwrite(filename, src);
I have Gaussian blur commented out because (counter intuitively) it was greatly increasing the number of equally inaccurate circles found.
Is there anything wrong with my input image that would cause Hough to not work as well as I expect? Are my parameters way off?
EDIT: first answer brought up a good point about the min/max radius hint for Hough. I resisted adding those parameters as the example image in this post is just one of thousands of images all with varying radii from ~20 to almost infinity.
I've adjusted my RANSAC algorithm from this answer: Detect semi-circle in opencv
Idea:
choose randomly 3 points from your binary edge image
create a circle from those 3 points
test how "good" this circle is
if it is better than the previously best found circle in this image, remember
loop 1-4 until some number of iterations reached. then accept the best found circle.
remove that accepted circle from the image
repeat 1-6 until you have found all circles
problems:
at the moment you must know how many circles you want to find in the image
tested only for that one image.
c++ code
result:
code:
inline void getCircle(cv::Point2f& p1,cv::Point2f& p2,cv::Point2f& p3, cv::Point2f& center, float& radius)
{
float x1 = p1.x;
float x2 = p2.x;
float x3 = p3.x;
float y1 = p1.y;
float y2 = p2.y;
float y3 = p3.y;
// PLEASE CHECK FOR TYPOS IN THE FORMULA :)
center.x = (x1*x1+y1*y1)*(y2-y3) + (x2*x2+y2*y2)*(y3-y1) + (x3*x3+y3*y3)*(y1-y2);
center.x /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
center.y = (x1*x1 + y1*y1)*(x3-x2) + (x2*x2+y2*y2)*(x1-x3) + (x3*x3 + y3*y3)*(x2-x1);
center.y /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
radius = sqrt((center.x-x1)*(center.x-x1) + (center.y-y1)*(center.y-y1));
}
std::vector<cv::Point2f> getPointPositions(cv::Mat binaryImage)
{
std::vector<cv::Point2f> pointPositions;
for(unsigned int y=0; y<binaryImage.rows; ++y)
{
//unsigned char* rowPtr = binaryImage.ptr<unsigned char>(y);
for(unsigned int x=0; x<binaryImage.cols; ++x)
{
//if(rowPtr[x] > 0) pointPositions.push_back(cv::Point2i(x,y));
if(binaryImage.at<unsigned char>(y,x) > 0) pointPositions.push_back(cv::Point2f(x,y));
}
}
return pointPositions;
}
float verifyCircle(cv::Mat dt, cv::Point2f center, float radius, std::vector<cv::Point2f> & inlierSet)
{
unsigned int counter = 0;
unsigned int inlier = 0;
float minInlierDist = 2.0f;
float maxInlierDistMax = 100.0f;
float maxInlierDist = radius/25.0f;
if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
if(maxInlierDist>maxInlierDistMax) maxInlierDist = maxInlierDistMax;
// choose samples along the circle and count inlier percentage
for(float t =0; t<2*3.14159265359f; t+= 0.05f)
{
counter++;
float cX = radius*cos(t) + center.x;
float cY = radius*sin(t) + center.y;
if(cX < dt.cols)
if(cX >= 0)
if(cY < dt.rows)
if(cY >= 0)
if(dt.at<float>(cY,cX) < maxInlierDist)
{
inlier++;
inlierSet.push_back(cv::Point2f(cX,cY));
}
}
return (float)inlier/float(counter);
}
float evaluateCircle(cv::Mat dt, cv::Point2f center, float radius)
{
float completeDistance = 0.0f;
int counter = 0;
float maxDist = 1.0f; //TODO: this might depend on the size of the circle!
float minStep = 0.001f;
// choose samples along the circle and count inlier percentage
//HERE IS THE TRICK that no minimum/maximum circle is used, the number of generated points along the circle depends on the radius.
// if this is too slow for you (e.g. too many points created for each circle), increase the step parameter, but only by factor so that it still depends on the radius
// the parameter step depends on the circle size, otherwise small circles will create more inlier on the circle
float step = 2*3.14159265359f / (6.0f * radius);
if(step < minStep) step = minStep; // TODO: find a good value here.
//for(float t =0; t<2*3.14159265359f; t+= 0.05f) // this one which doesnt depend on the radius, is much worse!
for(float t =0; t<2*3.14159265359f; t+= step)
{
float cX = radius*cos(t) + center.x;
float cY = radius*sin(t) + center.y;
if(cX < dt.cols)
if(cX >= 0)
if(cY < dt.rows)
if(cY >= 0)
if(dt.at<float>(cY,cX) <= maxDist)
{
completeDistance += dt.at<float>(cY,cX);
counter++;
}
}
return counter;
}
int main()
{
//RANSAC
cv::Mat color = cv::imread("HoughCirclesAccuracy.png");
// convert to grayscale
cv::Mat gray;
cv::cvtColor(color, gray, CV_RGB2GRAY);
// get binary image
cv::Mat mask = gray > 0;
unsigned int numberOfCirclesToDetect = 2; // TODO: if unknown, you'll have to find some nice criteria to stop finding more (semi-) circles
for(unsigned int j=0; j<numberOfCirclesToDetect; ++j)
{
std::vector<cv::Point2f> edgePositions;
edgePositions = getPointPositions(mask);
std::cout << "number of edge positions: " << edgePositions.size() << std::endl;
// create distance transform to efficiently evaluate distance to nearest edge
cv::Mat dt;
cv::distanceTransform(255-mask, dt,CV_DIST_L1, 3);
unsigned int nIterations = 0;
cv::Point2f bestCircleCenter;
float bestCircleRadius;
//float bestCVal = FLT_MAX;
float bestCVal = -1;
//float minCircleRadius = 20.0f; // TODO: if you have some knowledge about your image you might be able to adjust the minimum circle radius parameter.
float minCircleRadius = 0.0f;
//TODO: implement some more intelligent ransac without fixed number of iterations
for(unsigned int i=0; i<2000; ++i)
{
//RANSAC: randomly choose 3 point and create a circle:
//TODO: choose randomly but more intelligent,
//so that it is more likely to choose three points of a circle.
//For example if there are many small circles, it is unlikely to randomly choose 3 points of the same circle.
unsigned int idx1 = rand()%edgePositions.size();
unsigned int idx2 = rand()%edgePositions.size();
unsigned int idx3 = rand()%edgePositions.size();
// we need 3 different samples:
if(idx1 == idx2) continue;
if(idx1 == idx3) continue;
if(idx3 == idx2) continue;
// create circle from 3 points:
cv::Point2f center; float radius;
getCircle(edgePositions[idx1],edgePositions[idx2],edgePositions[idx3],center,radius);
if(radius < minCircleRadius)continue;
//verify or falsify the circle by inlier counting:
//float cPerc = verifyCircle(dt,center,radius, inlierSet);
float cVal = evaluateCircle(dt,center,radius);
if(cVal > bestCVal)
{
bestCVal = cVal;
bestCircleRadius = radius;
bestCircleCenter = center;
}
++nIterations;
}
std::cout << "current best circle: " << bestCircleCenter << " with radius: " << bestCircleRadius << " and nInlier " << bestCVal << std::endl;
cv::circle(color,bestCircleCenter,bestCircleRadius,cv::Scalar(0,0,255));
//TODO: hold and save the detected circle.
//TODO: instead of overwriting the mask with a drawn circle it might be better to hold and ignore detected circles and dont count new circles which are too close to the old one.
// in this current version the chosen radius to overwrite the mask is fixed and might remove parts of other circles too!
// update mask: remove the detected circle!
cv::circle(mask,bestCircleCenter, bestCircleRadius, 0, 10); // here the radius is fixed which isnt so nice.
}
cv::namedWindow("edges"); cv::imshow("edges", mask);
cv::namedWindow("color"); cv::imshow("color", color);
cv::imwrite("detectedCircles.png", color);
cv::waitKey(-1);
return 0;
}
If you'd set minRadius and maxRadius paramaeters properly, it'd give you good results.
For your image, I tried following parameters.
method - CV_HOUGH_GRADIENT
minDist - 100
dp - 1
param1 - 80
param2 - 10
minRadius - 250
maxRadius - 300
I got the following output
Note: I tried this in C++.
I am using this method with AndEngine to add a sprite to the screen and make it move across the screen.
private void addFace() {
Random rand = new Random();
float x = (int) mCamera.getHeight() - mBallTextureRegion.getHeight();
float minY = mBallTextureRegion.getHeight();
float maxY = (int)(mCamera.getWidth() + mBallTextureRegion.getWidth());
float rangeY = maxY - minY;
float y = rand.nextInt((int)rangeY) + minY;
this.mFaceCount++;
Log.e("Faces: ", "Face" + this.mFaceCount);
Sprite face = null;
Body body = null;
face = new Sprite(x, y, this.mBallTextureRegion.clone());
body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF);
this.mScene.attachChild(face);
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face, body, true, true));
int minDuration = 2;
int maxDuration = 4;
int rangeDuration = maxDuration - minDuration;
int actualDuration = rand.nextInt(rangeDuration) + minDuration;
MoveXModifier mod = new MoveXModifier(actualDuration, face.getX(), - face.getWidth());
face.registerEntityModifier(mod);
}
What i would like to do is, instead of the Random position being selected and the sprite being added to the left side of the Scene, i would like for it to be added to the Top and fall down.
I basically want to flip the direction,
I just cant figure out how. Everything i tried was no luck.
Any ideas or suggestions?
While I don't know much about Andengine, I suspect you would want to change these lines:
float x = (int) mCamera.getHeight() - mBallTextureRegion.getHeight();
float y = rand.nextInt((int)rangeY) + minY;
MoveXModifier mod = new MoveXModifier(actualDuration, face.getX(), - face.getWidth());
Flip the x and y statements, but set y to -mBallTextureRegion.getHeight() if you want it to appear outside the screen.
For the MoveXModifier, I would guess there is a corresponding MoveYModifier (use face.getY() and -face.getHeight() respectively)