I am creating an Android App in Kotlin in which I want to remove background from a person's portrait image in real time.
(This code is meant to be imbedded in a video calling app whose one of the feature is to remove person's background during video calls for privacy issues.)
I have downloaded the starter app of TensorFlow Lite from here. It is generating a mask and an overlay of mask and the captured image. How can we use that mask to take a cut-out of the person and replace background with any image from the gallery.
I haven't done any work with TensorFlow Lite in the past, so any help would be highly appreciated.
Thanks in advance.
The project of TF Lite works fine and you get the generated mask from input image... Then you have to use something different to achieve the desired result. Just check out PorterDuff.Mode!
I have created for you a helper function to use it and get the desired bitmap. From there you can continue and load it inside an ImageView:
fun cropBitmapWithMask(original: Bitmap, mask: Bitmap?): Bitmap? {
if (mask == null
) {
return null
}
val w = original.width
val h = original.height
if (w <= 0 || h <= 0) {
return null
}
val styled = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
val canvas = Canvas(styled)
val paint =
Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawBitmap(original, 0f, 0f, null)
canvas.drawBitmap(mask, 0f, 0f, paint)
paint.xfermode = null
return styled
}
Tag me if you have more questions.
Cheers
Related
I am making sharing feature like youtube music shares image to instagram story. Everything works fine, but I can't make the curved edge.
The transparent part at the end of the edge changes to black like the below picture.
I tried
add bitmap.setHasAlpha(true)
custom shading with BitmapShader
set AntiAlias true
My code is like this
fun onCreate() {
val changedBitmap = cardViewToBitmap(binding.clCardview)
shareImage(changedBitmap)
}
fun cardViewToBitmap(cardView: View): Bitmap {
val bitmap = Bitmap.createBitmap(cardView.width, cardView.height, Bitmap.Config.ARGB_8888)
bitmap.eraseColor(Color.TRANSPARENT)
bitmap.setHasAlpha(true)
val canvas = Canvas(bitmap)
val radius = 20f
val path = Path()
path.addRoundRect(0f, 0f, cardView.width.toFloat(), cardView.height.toFloat(), radius, radius, Path.Direction.CW)
canvas.clipPath(path)
cardView.draw(canvas)
return bitmap
}
I found that instagram doesn't support transparent background. How can I do it?
What I'm trying to do
I'm trying to implement a view that can be masked. Let's say the user has an image like this:
which gets masked by a custom shape like this:
The resulting image should only let the shoe shine through and make the background transparent.
Implementation
I have tried to implement it this way:
Make all black pixels in the mask transparent
Override the View's draw function to draw the mask over the original image using an xfermode (PorterDuffXfermode) of DST_IN. (See Android docs for PorterDuff.Mode)
This works perfectly fine and gives me this image (green pixels mean transparent):
While this works perfectly fine, I couldn't implement a custom "drawing" functionality to let the user draw or erase the mask. I only succeeded in doing one or the other, but not both at the same time. This is what I achieved right now:
note that erasing the mask works as expected, but trying to extend the mask doesn't work and paints white pixels (instead of letting the original image (shoe) shine through).
This is the code I'm using right now:
override fun draw(baseCanvas: Canvas) {
super.draw(baseCanvas)
val image = imageBitmap
val mask = maskBitmap
val drawingBitmap = drawingBitmap
if (image != null && mask != null && drawingBitmap != null) {
run {
val canvas = Canvas(drawingBitmap)
// 1. Fill with white
canvas.drawColor(Color.WHITE)
// 2. Draw mask and only let non-transparent pixels through
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawBitmap(mask, 0f, 0f, paint)
// 3. Draw all lines point to point with white color and custom xfermode
paint.xfermode = null
paint.style = Paint.Style.STROKE
paint.color = Color.WHITE
paint.strokeWidth = penSize
paint.isDither = true
paint.strokeJoin = Paint.Join.ROUND
paint.strokeCap = Paint.Cap.ROUND
paint.pathEffect = CornerPathEffect(10f)
paint.isAntiAlias = true
lines.forEach { line ->
paint.xfermode = when (line.drawMode) {
DrawMode.ERASE -> PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
DrawMode.DRAW -> PorterDuffXfermode(PorterDuff.Mode.SRC)
}
val path = Path().also { path ->
val points = line.points
val range = points.size - 1
for (i in 1..range) {
path.moveTo(points[i - 1].x, points[i - 1].y)
path.lineTo(points[i].x, points[i].y)
}
}
canvas.drawPath(path, paint)
}
}
run {
val canvas = Canvas(image)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
canvas.drawBitmap(drawingBitmap, 0f, 0f, paint)
}
}
}
Important variables in scope:
imageBitmap: The shoe bitmap
maskBitmap: The bitmap that contains white colors for the original shape to shine through, transparent for everything that should be transparent
drawingBitmap: An empty bitmap with the same size as imageBitmap, I use this for drawing only and then draw that result onto the imageBitmap (using a Canvas, see second run block)
lines: The lines I want to draw. A line consists of a drawMode (draw or erase) and a list of all points I have tracked.
Weird Observation
The thing that's confusing me right now is that when I fill the canvas black before adding the image:
run {
val canvas = Canvas(image)
canvas.drawColor(Color.BLACK) // <-- ADD THIS HERE
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
canvas.drawBitmap(drawingBitmap, 0f, 0f, paint)
}
it looks like it's working fine:
Same result if I fill the canvas RED, it lets the Red color shine through instead of Black. Why does it work with the color, but not the original bitmap?
It even looks correct if inspected in the debugger:
Question
Does anyone here know why this does not work as I expect it to work? I tried to play around with all kinds of different PorterDuff modes, but couldn't manage to get it working smoothly.
Any help appreciated!
Okay I got it working. I'm not sure if there's a more efficient way to achieve this, but this is what I did:
Create a fourth Bitmap called imageDrawingBitmap.
Set imageDrawingBitmap to the actual ImageView
As in the code snippet above, first draw the mask, then paths ontop of that
Then create a Canvas for the imageDrawingBitmap
Draw original image in that Canvas
Draw mask (maskDrawingBitmap from step 3.) in that Canvas with PorterDuff DST_ATOP.
That seems to be working fine.
I am previewing camera feed to texture view using cameraX. How to draw a rectangle on the screen using coordinates that i have? I don't want a complex function. I simply want to draw a rectangle.
I am using kotlin and android studio 4.0.
I would go for an ImageView overlapping on top of the Textureview at the same xml. This imageview will load a transparent bitmap that will have only the rectangle drawn. If you have the coordinates u have to do:
val myRectPaint = Paint()
myRectPaint.strokeWidth = 5F
myRectPaint.color = Color.RED
myRectPaint.style = Paint.Style.STROKE
// Create a Canvas object for drawing on the original bitmap provided
val tempBitmap =
Bitmap.createBitmap(bitmap!!.width, bitmap.height, Bitmap.Config.ARGB_8888)
val tempCanvas = Canvas(tempBitmap)
tempCanvas.drawBitmap(bitmap, 0F, 0F, null)
tempCanvas.drawRoundRect(
RectF(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat()),
2f,
2f,
myRectPaint
)
// Use this to widen picture on top or bottom
val croppedFaceBitmap =
Bitmap.createBitmap(tempBitmap, x1, y1, x2, y2)
In any case you can also follow this example from tensorflow github where round boxes are drawn when object is detected.
Hope I helped
I have an application where there is an Image view and a Edit text in android. Now whenever the user has entered the text in edittext, the text will appear on the top of imageview as a watermark text. I am able to achieve this thing, but i am not able to put a drag and drop functionality to that watermark text. I want the user to drag the watermark text and can drop anywhere inside the Image view.
Below is my code to redraw the Imageview with watermark Text:
Could anyone please help me in this, as I am stuck on this issue from past two days?
Thanks in advance.
fun redrawImage(): Bitmap? {
val w = src.width
val h = src.height
val result = Bitmap.createBitmap(w, h, src.config)
val canvas = Canvas(result)
canvas.drawBitmap(src, 0F, 0F, null)
val paint = Paint()
paint.setColor(Color.BLACK)
paint.setAlpha(100)
paint.setTextSize(26F)
paint.setAntiAlias(true)
paint.setUnderlineText(true)
canvas.drawText(waterMarkEditText.text.toString(), 0F , 0F, paint)
return result
}
I am calling the above method and passing the Image View bitmap to it to add the watermark text to the imageview.
I need to convert my SurfaceView's content into a Bitmap. I tried to achieve this using the following code snippet inside the SurfaceView:
public final Bitmap getScreenCopy() {
Bitmap bitmap = Bitmap.createBitmap(
getWidth(),
getHeight(),
Bitmap.Config.ARGB_8888
);
Canvas temporaryCanvas = new Canvas(bitmap);
draw(temporaryCanvas); // Voodoo.
return bitmap;
}
The bitmap I receive from that drone seems to be transparent, does anyone know how to fix that?
Using a TextureView is not possible, since I use the Parrot SDK and the drone needs a SurfaceView to display the Frames.
I've been trying to find a way to achieve this too and so far I haven't found a way to do it for devices with API level below 24. I tried messing with the drawing cache, but I get nothing but transparency. I didn't expect the drawingCache approach to work anyway due to the fact that the view itself draws nothing and everything goes onto the Surface.
For API levels 24 and newer, you can use the PixelCopy APIs to copy all or a portion of the SurfaceView into a bitmap.
Here's a snippet from my (Kotlin) code:
override fun getPlotBitmap(view: PlotSurfaceView) = Single.create<Bitmap> {
val plotBitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val listener = PixelCopy.OnPixelCopyFinishedListener { copyResult ->
when (copyResult) {
PixelCopy.SUCCESS -> {
it.onSuccess(plotBitmap)
}
else -> {
it.onError(RuntimeException("Pixel copy failed with result $copyResult"))
}
}
}
PixelCopy.request(view, plotBitmap, listener, view.handler)
}