I am detecting a face in my app. I use two ImageView, the first for the face and the second for a mask. I need set the mask position over the calculated face position and then can move the mask with touch event. For the touch event I did use of this tutorial: http://blahti.wordpress.com/2011/01/17/moving-views-part-2/
The method for mask scalling can return the face position. I tried set the XY position to the mask Imageview but ever is showed in the position 0.0. over the face. Then I can move the mask ImageView over the face. "view" is my mask. Ideally I need initialize the mask imageview over the face position.
PD: Sorry my english is bad.
protected Bitmap draw(int mode) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.c10);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Point position = new Point();
xRatio = anchoCara * 1.0f / anchoCara;
yRatio = altoCara * 1.0f / altoCara;
Bitmap resizedBitmap = null;
float factor = 9.6f;//9.6
for (int i = 0; i < eyesMidPts.length; i++) {
if (eyesMidPts[i] != null) {
pOuterBullsEye.setStrokeWidth(eyesDistance[i] / 6);
float newWidth = eyesDistance[i] * factor;
float newHeight = eyesDistance[i] * factor;
float scaleWidth = (newWidth) / width;
float scaleHeight = (newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
height, matrix, true);
position.set(
(int) ((eyesMidPts[i].x * xRatio) - (eyesDistance[i] * factor) ),//2
(int) ((eyesMidPts[i].y * yRatio) - (eyesDistance[i] * factor) ));
Log.e("Face", "positio x : " + position.x);
Log.e("Face", "positio y : " + position.y);
Log.e("Face", "mascara width : " + resizedBitmap.getWidth());
Log.e("Face", "mascara heigth : " + resizedBitmap.getHeight());
// view.setBackgroundDrawable(new BitmapDrawable(resizedBitmap));
// view.setAdjustViewBounds(true);
// Log.i("Face", "positio x : " + view.getScrollX());
// Log.i("Face", "positio y : " + view.getScrollY());
}
}
return resizedBitmap;
}
Finally I did using the next code:
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
faceDetect(MODO_VIEW);
Display m=getWindowManager().getDefaultDisplay();
anchoPant=m.getWidth();
Log.i("Face","separacion derecha: "+((anchoPant-cara.getWidth())/2)+"px" );
MyAbsoluteLayout.LayoutParams mp=new MyAbsoluteLayout.LayoutParams(MyAbsoluteLayout.LayoutParams.WRAP_CONTENT, MyAbsoluteLayout.LayoutParams.WRAP_CONTENT, (anchoPant-cara.getWidth())/2, 0);
cara.setLayoutParams(mp);
//view.setBackgroundDrawable(new BitmapDrawable(draw(MODO_VIEW)));
view.setImageBitmap(draw(MODO_VIEW));
MyAbsoluteLayout.LayoutParams lp=new MyAbsoluteLayout.LayoutParams(MyAbsoluteLayout.LayoutParams.WRAP_CONTENT,MyAbsoluteLayout.LayoutParams.WRAP_CONTENT,position.x+((anchoPant-cara.getWidth())/2),position.y);
view.setLayoutParams(lp);
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
In my question, the method can return the position, Then using MyAbsoluteLayout.LayoutParams over OnGlobalLayout() I can set XY Position without problems.
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.
I have an rotated textview and I want to drag and drop this view.
The problem is that the drag shadow has no rotation.
I found a solution for android in java but this does not work for me.
Maybe I translate the code wrong
How to drag a rotated DragShadow?
class CustomDragShdowBuilder : View.DragShadowBuilder
{
private View _view;
public CustomDragShdowBuilder(View view)
{
_view = view;
}
public override void OnDrawShadow(Canvas canvas)
{
double rotationRad = Math.ToRadians(_view.Rotation);
int w = (int)(_view.Width * _view.ScaleX);
int h = (int)(_view.Height * _view.ScaleY);
double s = Math.Abs(Math.Sin(rotationRad));
double c = Math.Abs(Math.Cos(rotationRad));
int width = (int)(w * c + h * s);
int height = (int)(w * s + h * c);
canvas.Scale(_view.ScaleX, _view.ScaleY, width / 2, height / 2);
canvas.Rotate(_view.Rotation, width / 2, height / 2);
canvas.Translate((width - _view.Width) / 2, (height - _view.Height) / 2);
base.OnDrawShadow(canvas);
}
public override void OnProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint)
{
shadowTouchPoint.Set(shadowSize.X / 2, shadowSize.Y / 2);
base.OnProvideShadowMetrics(shadowSize, shadowTouchPoint);
}
}
I found a solution for android in java but this does not work for me. Maybe I translate the code wrong
Yes, you are translating it wrong, you did change the codes of OnDrawShadow but you didn't pay attention to OnProvideShadowMetrics, which aims at changing the size of the canvas drawing area, so you need to pass the same width and height that has been calculated by codes:
Here is the modified version of DragShdowBuilder:
public class MyDragShadowBuilder : DragShadowBuilder
{
private int width, height;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v) : base(v)
{
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the system.
public override void OnProvideShadowMetrics(Android.Graphics.Point outShadowSize, Android.Graphics.Point outShadowTouchPoint)
{
double rotationRad = Java.Lang.Math.ToRadians(View.Rotation);
int w = (int)(View.Width * View.ScaleX);
int h = (int)(View.Height * View.ScaleY);
double s = Java.Lang.Math.Abs(Java.Lang.Math.Sin(rotationRad));
double c = Java.Lang.Math.Abs(Java.Lang.Math.Cos(rotationRad));
//calculate the size of the canvas
//width = view's width*cos(rad)+height*sin(rad)
width = (int)(w * c + h * s);
//height = view's width*sin(rad)+height*cos(rad)
height = (int)(w * s + h * c);
outShadowSize.Set(width, height);
// Sets the touch point's position to be in the middle of the drag shadow
outShadowTouchPoint.Set(outShadowSize.X / 2, outShadowSize.Y / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
public override void OnDrawShadow(Canvas canvas)
{
canvas.Scale(View.ScaleX, View.ScaleY, width/2 , height/2);
//canvas.DrawColor(Android.Graphics.Color.White);
canvas.Rotate(View.Rotation,width/2, height / 2);
canvas.Translate((width - View.Width)/2, (height - View.Height) / 2);
base.OnDrawShadow(canvas);
}
}
And here is the complete sample:RotatedTextViewSample
In my android app.I want to make image Straightening edit feature using android-gpuimage library but GPUImageView doesn't give feature of getBitmap() or setMatrix() then how is it possible please let me know? here is the code to review :
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(isStraightenEffectEnabled){
float angle = (progress - 45);
float width = mGPUImageView.getWidth();
float height = mGPUImageView.getHeight();
if (width > height) {
width = mGPUImageView.getHeight();
height = mGPUImageView.getWidth();
}
float a = (float) Math.atan(height/width);
// the length from the center to the corner of the green
float len1 = (width / 2) / (float) Math.cos(a - Math.abs(Math.toRadians(angle)));
// the length from the center to the corner of the black
float len2 = (float) Math.sqrt(Math.pow(width/2,2) + Math.pow(height/2,2));
// compute the scaling factor
float scale = len2 / len1;
Matrix matrix = mGPUImageView.getMatrix();
if (mMatrix == null) {
mMatrix = new Matrix(matrix);
}
matrix = new Matrix(mMatrix);
float newX = (mGPUImageView.getWidth() / 2) * (1 - scale);
float newY = (mGPUImageView.getHeight() / 2) * (1 - scale);
matrix.postScale(scale, scale);
matrix.postTranslate(newX, newY);
matrix.postRotate(angle, mGPUImageView.getWidth() / 2, mGPUImageView.getHeight() / 2);
Is it possible to get mGPUImageView.setMatrix(matrix);
NOW HERE getMatrix() is a method of GPUImageView but setMatrix() or getBitmap() is not method available with GPUImageView class. Any workarounds if possible ?
add getGPUImage in GPUImageView class
public GPUImage getGPUImage() {
return mGPUImage;
}
then get you can get bitmap like this:
mGPUImageView.getGPUImage().getBitmapWithFilterApplied();
You can also get bitmap like this:
Bitmap bitmap = mGPUImageView.capture();
You can get filtered bitmap renderer and Pixelbuffer. This might be helpful for you.
GPUImageLookupFilter amatorka = new GPUImageLookupFilter();
amatorka.setBitmap(BitmapFactory.decodeResource(getResources(), getResources().getIdentifier("fil_" + position, "drawable", getPackageName())));
GPUImageRenderer renderer = new GPUImageRenderer(amatorka);
renderer.setImageBitmap(bitmap, false);
PixelBuffer buffer = new PixelBuffer(80, 80);
buffer.setRenderer(renderer);
buffer.getBitmap();
I am trying to put a watermark/Image on another image, we can zoom in, zoom out, drag and rotate the watermark image by fingertouch. I am using Open CV library to rotate the image as suggested in this post
image rotation with opencv in android cuts off the edges of an image
It works fine until the rotation angle gets in between -75 to -105 and 75 to 105. In this range my image get cropped or it changes its position. I have tried several ways to get the right center point for the watermark image to put it in right position after rotating it but failed to do so.
Here is the code to rotate the image
// CALCULATE ROTATED WATERMARK IMAGE
private void CreateRotatedWaterMark()
{
// Means rotation took place
if (waterMarkAngle > 0 || waterMarkAngle < 0) {
// calculation for new width/height of rotated watermark image
newWidthHeight = boundingWaterMarkRect();
// remove when done testing
double pivotX = newWidthHeight[0] / 2; // 0 is width
double pivotY = newWidthHeight[1] / 2; // 1 is height
// rotating water image
org.opencv.core.Point center;
Size targetSize;
Mat targetMat;
// scalar is color/ RGBA , but alpha doesn't seem to work
Scalar colorScalar = new Scalar(255, 190, 190, 0);
double offsetX = (newWidthHeight[0] - scaledImage.width()) / 2;
double offsetY = (newWidthHeight[1] - scaledImage.height()) / 2;
// watermark's rotation lays somewhere in between 75' AND 105' OR
// -75' AND -105'
Log.e(TAG, "check => offsetX/offsetY Before => " + offsetX + " / "
+ offsetY);
if (offsetX < 0 || offsetY < 0) {
//this gets true when angle of rotation gets between -75 to -105 and 75 to 105
// change the offsets, now new newWidth < oldWidth AND newHeight
// > oldHeight (could be vice versa)
offsetX = (newWidthHeight[0] - scaledImage.height()) / 2;
offsetY = (newWidthHeight[1] - scaledImage.width()) / 2;
// Declaring new target size and target Mat, so rotated image is
// placed in new target Mat
targetSize = new Size(newWidthHeight[1], newWidthHeight[0]);
targetMat = new Mat(targetSize, scaledImage.type(), colorScalar);
// Getting the reference of center area from target Mat,
// below we're copying the actual image (scaledImage) to center
// position to target Mat
Mat waterSubmat = targetMat.submat((int) offsetX, (int) offsetX
+ scaledImage.height(), (int) offsetY, (int) offsetY
+ scaledImage.width());
scaledImage.copyTo(waterSubmat);
// Writing target image to sdcard, so we can know if its working
// properly, remove it when done with testing
Highgui.imwrite("mnt/sdcard/scaled90.png", targetMat);
Highgui.imwrite("mnt/sdcard/waterSubmat.png", waterSubmat);
Highgui.imwrite("mnt/sdcard/ScaledImage.png", scaledImage);
// targetSize is reverted again, so that target mat is pasted on
// canvas image
targetSize = new Size(newWidthHeight[0], newWidthHeight[1]);
pivotX = newWidthHeight[0] / 2;
pivotY = newWidthHeight[1] / 2;
Log.e(TAG, "check => pivotX/pivotY => " + pivotX + " / "
+ pivotY);
Log.e(TAG, "check => Angle => " + waterMarkAngle);
center = new org.opencv.core.Point(pivotX, pivotY);
} else {
center = new org.opencv.core.Point(pivotX, pivotY);
targetSize = new Size(newWidthHeight[0], newWidthHeight[1]);
targetMat = new Mat(targetSize, scaledImage.type(), colorScalar);
// Centralizing watermark
Mat waterSubmat = targetMat.submat((int) offsetY, (int) offsetY
+ scaledImage.height(), (int) offsetX, (int) offsetX
+ scaledImage.width());
scaledImage.copyTo(waterSubmat);
Highgui.imwrite("mnt/sdcard/scaled10.png", targetMat);
}
Mat rotImage = Imgproc.getRotationMatrix2D(center, waterMarkAngle,
1.0); // 1.0 means scale 100%
Highgui.imwrite("mnt/sdcard/Rotated90.png", rotImage);
// Mat waterSubmat1 = rotImage.submat(0, scaledImage.height(), 0,
// scaledImage.width());
Mat resultMat = new Mat();
// scalar for color, Imagproc.warAffine actually rotates the final
// watermark on canvas image
colorScalar = new Scalar(122, 22, 22);
Imgproc.warpAffine(targetMat, resultMat, rotImage, targetSize,
Imgproc.INTER_LINEAR, Imgproc.BORDER_CONSTANT, colorScalar);
Log.i(TAG, "check MATRIX = " + resultMat.toString());
Log.i(TAG, "check Result Mat " + resultMat.size().toString());
scaledImage = resultMat.clone();
Highgui.imwrite("mnt/sdcard/Result100.png", targetMat);
// Log.i(TAG, "check Result postion = "+ resultMat.r);
// WRITE SOME TEXT ON CANVAS IMAGE, its not rotated
Core.putText(scaledImage, "angle: " + waterMarkAngle,
new org.opencv.core.Point(0, scaledImage.height() / 2),
Core.FONT_HERSHEY_TRIPLEX, 2, new Scalar(23, 12, 450, 0.5),
2); // CV_BLUR_NO_SCALE
} else {
// CHANGE OPACITY OF WATERMARK IMAGE
// Core.multiply(scaledImage, new Scalar(1, 1, 1, 0.6),
// scaledImage);
// REMOVE THIS WHEN DONE TESTING
// Highgui.imwrite("mnt/sdcard/opacityWatermark.png",scaledImage);
}
}
private double[] boundingWaterMarkRect() {
// -----NEW CALCULATION------
// so we always get positive angle
double tempAngle = ((waterMarkAngle < 0 ? 360 - waterMarkAngle : waterMarkAngle) % 360);
double radians = Math.toRadians(tempAngle);
Log.i(TAG, "check => radian => "+ radians);
double sin = Math.abs(Math.sin(radians));
double cos = Math.abs(Math.cos(radians));
double newWidth = (double) (scaledImage.width() * cos + scaledImage.height() * sin);
double newHeight = (double) (scaledImage.width() * sin + scaledImage.height() * cos);
double[] wh = {newWidth, newHeight};
return wh;
}
Following are two images first one is right and the other is rotated wrong
Anyone have got solution for it?
How to get ImageView's Height and Width after Zoom in Zoom out.
I am using the following code:
mRectSrc.left = (int)(panX * bitmapWidth - viewWidth / (zoomX * 2));
mRectSrc.top = (int)(panY * bitmapHeight - viewHeight / (zoomY * 2));
mRectSrc.right = (int)(mRectSrc.left + viewWidth / zoomX);
mRectSrc.bottom = (int)(mRectSrc.top + viewHeight / zoomY);
mRectDst.left = getLeft();
mRectDst.top = getTop();
mRectDst.right = getRight();
mRectDst.bottom = getBottom();
// Adjust source rectangle so that it fits within the source image.
if (mRectSrc.left < 0) {
mRectDst.left += -mRectSrc.left * zoomX;
mRectSrc.left = 0;
}
if (mRectSrc.right > bitmapWidth) {
mRectDst.right -= (mRectSrc.right - bitmapWidth) * zoomX;
mRectSrc.right = bitmapWidth;
}
if (mRectSrc.top < 0) {
mRectDst.top += -mRectSrc.top * zoomY;
mRectSrc.top = 0;
}
if (mRectSrc.bottom > bitmapHeight) {
mRectDst.bottom -= (mRectSrc.bottom - bitmapHeight) * zoomY;
mRectSrc.bottom = bitmapHeight;
}
canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mPaint);
So using This code i am getting zoom effect on image. How Can i Get Bitmap Height and width after zoom in and zoom out.
I tried looking for some inbuilt function for getting the size of the bitmap attached with the image view but couldn't find any. So, I made a workaround. Just store the initial height and width of the bitmap in some variable and then update these variable values with the scale factor.
This is how you can get the bitmap attached with the image(will always return the initial size):
Drawable d = imageView.getDrawable();
Bitmap bmp = ((BitmapDrawable)d).getBitmap();
float w = bmp.getWidth();
float h = bmp.getHeight();
Method to update the values:
public void updateSize(float scaleFactor)
{
w *= scaleFactor;
h *= scaleFactor;
}
You can call this method from where you are adding zoom to the image.