I'm setting up an eye detection android project and I want to draw probabilities using Canvas, I'm using Firebase ML kit for custom models
I have successfully drawn only one point.
I would like to draw points ( probabilities from tflite model that I have ).
I tried using those functions :
private void useInferenceResult(float[] probabilities) throws IOException {
// [START mlkit_use_inference_result]
String[] result=new String[80];
float x=0;
float y=0;
ArrayList<Point> listpoint= new ArrayList<Point>();
for (int i = 0; i < probabilities.length; i++) {
Log.i("MLKit", String.format("%1.4f", probabilities[i]));
x=probabilities[i];
y=probabilities[i+1];
Point p=new Point(x,y);
i=i+1;
p.setX(x);
p.setY(y);
Log.i("Information1 ","valeur 1 "+p.getX());
listpoint.add(p);
Log.i("Information2 ","valeur 2 "+p.getX());
}
for(int j=0;j<listpoint.size();j++){
Log.e("Information","work");
Log.e("Resultat","point_"+j+"("+listpoint.get(j).getX()+", "+listpoint.get(j).getY()+")");
float xx=listpoint.get(j).getX()*100;
float yy=listpoint.get(j).getY()*100;
drawpoint(image2,0.20958422f * 100,0.6274962f * 100,1);
drawpoint(image2, 0.20460524f * 100,0.6708223f * 100,1);
}
}
//drawpoint function
private void drawpoint(ImageView imageView,float x,float y, int raduis){
BitmapFactory.Options myOptions = new BitmapFactory.Options();
myOptions.inDither = true;
myOptions.inScaled = false;
myOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;// important
myOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.imgg,myOptions);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
Bitmap workingBitmap = Bitmap.createBitmap(bitmap);
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(mutableBitmap);
canvas.drawCircle(x,y, raduis, paint);
imageView = (ImageView)findViewById(R.id.imageView);
imageView.setAdjustViewBounds(true);
imageView.setImageBitmap(mutableBitmap);
}
but I didn't get any result just one point drawn.
How can I draw multiples points on imageView?
I solved my problem by changing some lines of code, I declare those variables on the top and I create objects after setContentView:
BitmapFactory.Options myOptions;
Canvas canvas;
Bitmap mutableBitmap;
Bitmap workingBitmap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_open= findViewById(R.id.btn_open);
image2= findViewById(R.id.imageView);
myOptions = new BitmapFactory.Options();
bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.image000880,myOptions);
paint= new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
workingBitmap = Bitmap.createBitmap(bitmap);
mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(mutableBitmap);
private void drawpoint(ImageView imageView,float x,float y, int raduis){
myOptions.inDither = true;
myOptions.inScaled = false;
myOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;// important
myOptions.inPurgeable = true;
// ArrayList<Point> list= new ArrayList<>();
canvas.drawCircle(x,y, raduis, paint);
imageView = (ImageView)findViewById(R.id.imageView);
imageView.setAdjustViewBounds(true);
imageView.setImageBitmap(mutableBitmap);
}
Hope this solution will help others who have the same situation
Simple ImageView Descendent class.
public class CiclesImageView extends AppCompatImageView {
private ArrayList<PointF> theDots = new ArrayList<>();
private Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
public CirclesImageView(Context context) {
super(context);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
drawDot(new PointF(event.getX(),event.getY()));
return performClick();
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
for (PointF pt:theDots)canvas.drawCircle(pt.x,pt.y,5,paint);
}
public void drawDot(PointF dotPoint){
theDots.add(dotPoint);
invalidate();
}
}
This imageView is used the same way you use any other imageView, except if you touch it, it will draw a circle on itself. If you want to draw your own circles, the drawDot(pointF) is public.
Related
I am trying to create an implementation of face-swap, across multiple images. These images are not taken from camera. They are .jpg files on the device.
I need to extract a part of an image of a face, using Google's Face Detection API. For this purpose, I extended ImageView:
public class FaceExtractorImageView extends ImageView {
Bitmap image;
Bitmap origImage;
Context context;
FaceDetector detector;
SparseArray<Face> faces;
List<Rect> faceRects;
boolean changed = false;
Face selectedFace;
public FaceExtractorImageView(Context context) {
super(context);
this.context = context;
detector = new FaceDetector.Builder(context)
.setProminentFaceOnly(false)
.setLandmarkType(FaceDetector.ALL_LANDMARKS)
.build();
faceRects = new ArrayList<>();
//The other constructors do the exact same thing.
}
#Override
public void setImageBitmap(final Bitmap img){
image = img;
selectedFace = null;
if(!changed)
origImage = img;
changed =true;
super.setImageBitmap(img);
faces = detector.detect(new Frame.Builder().setBitmap(image).build());
faceRects.clear();
for(int i=0;i<faces.size();i++)
faceRects.add(getFaceRect(i));
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
selectedFace = null;
switch(event.getAction()){
case MotionEvent.ACTION_UP:
for(int i=0;i<faceRects.size();i++)
if(faceRects.get(i) {
.contains((int)event.getX(),
(int)event.getY())) {
selectedFace = faces.valueAt(i);
}
invalidate();
}
return true;
}
});
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(selectedFace!=null){
Paint p = new Paint();
p.setStyle(Paint.Style.STROKE);
p.setColor(Color.RED);
canvas.drawRect(getFaceRect(selectedFace), p);
}
}
public Face getSelectedFace(){
return selectedFace;
}
public Bitmap getSelectedFaceBitmap() {
if(selectedFace==null)
return null;
int index = faces.indexOfValue(selectedFace);
return getFaceBitmapWithIndex(index);
}
//Returns Bitmap of face with index #index form the currently set image
public Bitmap getFaceBitmapWithIndex(int index) {
return getFaceBitmapWithIndex(image, index);
}
//Returns Bitmap of face with index #index form image #src
public Bitmap getFaceBitmapWithIndex(Bitmap src, int index) {
Rect r = getFaceRect(index);
Bitmap created = Bitmap.createBitmap(src, r.left, r.top, r.width(), r.height());
ImageView v = new ImageView(context);
v.setImageBitmap(created);
//new AlertDialog.Builder(context).setView(v).create().show();
return created;
}
public Rect getFaceRect(Face face){
Rect r= new Rect();
//The constant values used below are for cropping the original image.
r.left = (int) face.getPosition().x + (int) face.getWidth() / 20;
r.top = (int) face.getPosition().y + (int) face.getHeight() / 4;
r.right = r.left + (int) (face.getWidth() * 5.75 / 10);
r.bottom = r.top + (int) (face.getHeight() * 3 / 5);
return r;
}
//Get rect for face at index #index of image #img
public Rect getFaceRect(int index){
Face face = faces.valueAt(index);
if(face==null) {
Log.e("Face not found", "The image in the view does not have a face with index "+index);
return null;
}
return getFaceRect(face);
}
}
The above code does the following:
When the View is touched , it detects if the touch was on a face in the
image. If it was, a cropped box is drawn on the face using
invalidate().
The Rect given by the vision API is cropped using the method
getFaceRect().
The bitmap of the face is given by the method
getFaceBitmapWithIndex(), where the index is required if there are
multiple faces in the image. Otherwise, index is 0.
The problem:
The Rect I use when getting the bitmap of the face is the one given by getFaceRect(). However, I am not able to get the correct area. There is always some offset.
The images explain the situation:
Rect given by the Vision API:
Rect generated by getFaceRect(). This is also drawn in onDraw() when invalidate() is called.
The region that I get when getFaceBitmapWithIndex() is called:
There is something very simple that I'm missing here, but I'm not able to figure what that is. Any help would be appreciated.
I found the bug. Turns out, I didn't take care of the varying dimensions of different images, with respect to the View itself.
So, I put the code after the call to super in setImageBitmap inside the getViewTreeObserver().addOnGlobalLayoutListener and built a new DrawingCache there:
#Override
public void setImageBitmap(final Bitmap img){
if(!changed)
origImage = img;
changed =true;
super.setImageBitmap(img);
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
buildDrawingCache();
image = getDrawingCache();
selectedFace = null;
faces = detector.detect(new Frame.Builder().setBitmap(image).build());
faceRects.clear();
for (int i = 0; i < faces.size(); i++)
faceRects.add(getFaceRect(i));
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
selectedFace = null;
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
for (int i = 0; i < faceRects.size(); i++)
if (faceRects.get(i).contains((int) event.getX(), (int) event.getY())) {
//Toast.makeText(context, "Touched", Toast.LENGTH_LONG).show();
selectedFace = faces.valueAt(i);
}
invalidate();
}
return true;
}
});
}
});
}
I'm trying to create a rain effect in android. I have already created the sprite class, what I don't know is how to do that appear X amount of drops per second or the time of life. Can somebody help me?
Thanks.
This is my code
Sprite class:
public class Sprite_Rain {
private Bitmap rain_drop;
private Game game;
private int speed = 50;
int x, y = -100;
boolean position = true;
Paint paint;
public Sprite_Rain(Game game, Bitmap rain_drop){
this.game = game;
this.rain_drop= rain_drop;
paint = new Paint();
}
public void onDraw(Canvas canvas) {
if (position == true) {
Random random = new Random();
x = random.nextInt(canvas.getWidth());
position = false;
}
canvas.drawBitmap(rain_drop, x, y, null);
}
}
Game class:
public class Game extends SurfaceView {
private Bitmap rain_drop;
private Sprite_Rain sprite_rain;
Paint paint;
private List<Sprite_Rain> sprites = new ArrayList<Sprite_Rain>();
public Game(Context context, AttributeSet attrs) {
super(context, attrs);
rain_drop = BitmapFactory.decodeResource(getResources(), R.drawable.rain_drop);
sprite_rain = new Sprite_Rain(this, rain_drop);
paint = new Paint();
}
private Sprite_Rain createSprite(int resource) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resource);
return new Sprite_Rain(this, bmp);
}
#Override
protected void onDraw(Canvas canvas) {
paint.setColor(Color.BLACK);
int i = 0;
for (Sprite_Rain sprite : sprites) {
sprite.onDraw(canvas);
i++;
}
invalidate();
}
}
i googled coverflow code i found one useful and work great to me (im not professional in android development ), what i need it.
when click on each image it will stream predetermined mp3 song for each image and another click will stop it ,
so each image will have different mp3 song which all mp3 songs stored in app it self .
public class CoverFlowExample extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoverFlow coverFlow;
coverFlow = new CoverFlow(this);
coverFlow.setAdapter(new ImageAdapter(this));
ImageAdapter coverImageAdapter = new ImageAdapter(this);
//coverImageAdapter.createReflectedImages();
coverFlow.setAdapter(coverImageAdapter);
coverFlow.setSpacing(-25);
coverFlow.setSelection(4, true);
coverFlow.setAnimationDuration(1000);
setContentView(coverFlow);
}
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private FileInputStream fis;
private Integer[] mImageIds = {
R.drawable.aa,
R.drawable.aab,
R.drawable.aabb,
R.drawable.aabbb,
R.drawable.aac,
};
private ImageView[] mImages;
public ImageAdapter(Context c) {
mContext = c;
mImages = new ImageView[mImageIds.length];
}
public boolean createReflectedImages() {
//The gap we want between the reflection and the original image
final int reflectionGap = 4;
int index = 0;
for (int imageId : mImageIds) {
Bitmap originalImage = BitmapFactory.decodeResource(getResources(),
imageId);
int width = originalImage.getWidth();
int height = originalImage.getHeight();
//This will not scale but will flip on the Y axis
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
//Create a Bitmap with the flip matrix applied to it.
//We only want the bottom half of the image
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height/2,
width, height/2, matrix, false);
//Create a new bitmap with same width but taller to fit reflection
Bitmap bitmapWithReflection = Bitmap.createBitmap(width
, (height + height/2), Config.ARGB_8888);
//Create a new Canvas with the bitmap that's big enough for
//the image plus gap plus reflection
Canvas canvas = new Canvas(bitmapWithReflection);
//Draw in the original image
canvas.drawBitmap(originalImage, 0, 0, null);
//Draw in the gap
Paint deafaultPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap, deafaultPaint);
//Draw in the reflection
canvas.drawBitmap(reflectionImage,0, height + reflectionGap, null);
//Create a shader that is a linear gradient that covers the reflection
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff,
TileMode.CLAMP);
//Set the paint to use this shader (linear gradient)
paint.setShader(shader);
//Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
//Draw a rectangle using the paint with our linear gradient
canvas.drawRect(0, height, width,
bitmapWithReflection.getHeight() + reflectionGap, paint);
ImageView imageView = new ImageView(mContext);
imageView.setImageBitmap(bitmapWithReflection);
imageView.setLayoutParams(new CoverFlow.LayoutParams(120, 180));
imageView.setScaleType(ScaleType.MATRIX);
mImages[index++] = imageView;
}
return true;
}
public int getCount() {
return mImageIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
//Use this code if you want to load from resources
ImageView i = new ImageView(mContext);
i.setImageResource(mImageIds[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(430, 430));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
return i;
//return mImages[position];
}
/** Returns the size (0.0f to 1.0f) of the views
* depending on the 'offset' to the center. */
public float getScale(boolean focused, int offset) {
/* Formula: 1 / (2 ^ offset) */
return Math.max(0, 1.0f / (float)Math.pow(2, Math.abs(offset)));
}
}
}
I have an app whit an imageView, which is set by the user, either using his/her camera or file system. When the image is set I use this class to round the corners of the image:
public class DrawRoundImage extends Drawable {
private static final boolean USE_VIGNETTE = true;
private final float mCornerRadius;
private final RectF mRect = new RectF();
private final BitmapShader mBitmapShader;
private final Paint mPaint;
private final int mMargin;
public DrawRoundImage(Bitmap bitmap, float cornerRadius, int margin) {
mCornerRadius = cornerRadius;
mBitmapShader = new BitmapShader(bitmap,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(mBitmapShader);
mMargin = margin;
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(mMargin, mMargin, bounds.width() - mMargin, bounds.height() - mMargin);
if (USE_VIGNETTE) {
RadialGradient vignette = new RadialGradient(
mRect.centerX(), mRect.centerY() * 1.0f / 0.7f, mRect.centerX() * 1.3f,
new int[] { 0, 0, 0x7f000000 }, new float[] { 0.0f, 0.7f, 1.0f },
Shader.TileMode.CLAMP);
Matrix oval = new Matrix();
oval.setScale(1.0f, 0.7f);
vignette.setLocalMatrix(oval);
mPaint.setShader(
new ComposeShader(mBitmapShader, vignette, PorterDuff.Mode.SRC_OVER));
}
}
#Override
public void draw(Canvas canvas) {
canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}
Now I need to upload the image to my server, but since it is a drawable I can't quite figure how to turn it back to a Bitmap, which I convert like this:
Bitmap bitmap = bMap;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
I try to convert the drawable to a Bitmap like this:
Drawable drawable = imageButton1.getDrawable();
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
But it throws this error:
java.lang.ClassCastException: com.adrissa.klea.model.DrawRoundImage
cannot be cast to android.graphics.drawable.BitmapDrawable
How can i properly convert a drawable to a bitmap? When i google all i find is questions about drawable resources to bitmap.
Here is how you can convert drawable to bitmap:
Bitmap pic = BitmapFactory.decodeResource(getResources(), R.drawable.example);
#Kæmpe Klunker, look at this solution:
https://stackoverflow.com/a/10600736/4137318
Probably you have to override following methods in your DrawRoundImage class, to make this solution working:
#Override
public int getIntrinsicHeight() {
return (int) mRect.height();
}
#Override
public int getIntrinsicWidth() {
return (int) mRect.width();
}
I am trying to remove my last path drawn on my canvas. I have tryed so far to add my paths in a ArrayList but I didn't find any method to remove the path easilly from canvas.
public class PaintView extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private static final int TOUCH_TOLERANCE_DP = 20;
private static final int BACKGROUND = Color.TRANSPARENT;
private List<Point> mPoints = new ArrayList<Point>();
private int mLastPointIndex = 0;
private int mTouchTolerance;
private boolean isPathStarted = false;
private boolean canCreatePoints = true;
private Polygon poly;
private Builder build;
private ArrayList<Polygon> polyList;
private ArrayList<Path> undoPath, redoPath;
public PaintView(Context context) {
super(context);
mCanvas = new Canvas();
mPath = new Path();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
mTouchTolerance = dp2px(TOUCH_TOLERANCE_DP);
polyList = new ArrayList<Polygon>();
undoPath = new ArrayList<Path>();
redoPath = new ArrayList<Path>();
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth,
int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
clear();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(BACKGROUND);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawPath(mPath, mPaint);
for(Path p : undoPath) {
canvas.drawPath(p, mPaint);
}
for (Point point : mPoints) {
canvas.drawPoint(point.x, point.y, mPaint);
}
}
Below is onTouch method, I create lines using Path when variable canCreatePoints it's true.If canCreatePoints it's false I check in a ArrayList<Polygon> if my touch point is inside a polygon.
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(canCreatePoints == true)
{
create_point(x,y);
if( mPoints.size() > 1 )
{
// start point
Point p = mPoints.get(mLastPointIndex);
mPath.moveTo(p.x, p.y);
// end point
p = mPoints.get(mLastPointIndex + 1);
mPath.lineTo(p.x, p.y);
mCanvas.drawPath(mPath, mPaint);
undoPath.add(mPath);
mPath.reset();
// increment point index
++mLastPointIndex;
}
}
if(canCreatePoints == false)
{
Points pp = new Points(Math.round(x), Math.round(y));
boolean contains;
for(Polygon tempPoly:polyList){
contains = tempPoly.contains(pp);
if(contains == true)
{
Log.i("TEST","Poligonul contine punctul");
Toast.makeText(getContext(),"Test poly "+polyList.indexOf(tempPoly),
Toast.LENGTH_SHORT).show();
}
}
}
invalidate();
break;
}
return true;
}
private void create_point(float x, float y){
Point p = new Point(Math.round(x),Math.round(y));
mPoints.add(p);
}
Bellow I finnish my polygon form by action of a Button. I create a path from last point to first point and draw it on canvas.
public void finnishDraw(){
if( mPoints.size() > 1 )
{
// start point
Point p = mPoints.get(0);
mPath.moveTo(p.x, p.y);
// end point
p = mPoints.get( mPoints.size() - 1);
mPath.lineTo(p.x, p.y);
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
invalidate();
int x[] = new int[mPoints.size()];
int y[] = new int[mPoints.size()];
build = new Builder();
for(int i=0 ; i<mPoints.size() ; i++)
{
p = mPoints.get(i);
x[i] = p.x;
y[i] = p.y;
build.addVertex(new Points(x[i],y[i]));
Log.i("TEST","Adaug la builder punctele "+x[i]+" si "+y[i]);
}
poly = build.build();
polyList.add(poly);
mPoints.clear();
mLastPointIndex = 0;
}
}
So far I found only a solution with a back-up Bitmap before drawing a line but I didn't understand how it really works.
You can't undo a drawPath on a Canvas. From the documentation:
Via the Canvas, your drawing is actually performed upon an underlying
Bitmap, which is placed into the window.
As you say, you need a backup Bitmap to save the state of the current Bitmap before drawing on it, for example using a Memento pattern.
Take a look at Fast undo/redo for bitmap editor when memory is limited? and Fast undo facility for bitmap editor application (the last one targetting iPhone, but the underlying idea is the same)
Another approach less memory consuming could be saving the state of the objects that you want to draw on your canvas and draw them on demand (when you need to show a partial or a full result).
To remove the last path only apply this function:
public void clearLast(){
if(!paths.isEmpty()) {
backgroundColor = DEFAULT_BG_COLOR;
int size = paths.size();
paths.remove(paths.get(size-1));
normal();
invalidate();
}
}