OpenCV4Android Sample App - what does this code snippet do? - android

What is happening from line #129 to line #133 in this class of the Color blob detection sample app?
SOME CONTEXT:
The camera view in the app looks like this: (Notice that in the camera view, there is a black border around the camera frame)
(If you can't see the image, check it here.)
From Line 114 to 128, the following is happening.
int cols = mRgba.cols(); cols() gives the number of columns in a matrix. The matrix here is the Mat representing a frame in the live stream of frames being displayed (and not the entire camera view), i.e. it represents the part of the camera view where live stream is being displayed, EXCLUDING the black border of the camera view.
int rows = mRgba.rows(); rows() gives the number of rows in the camera frame, EXCLUDING the black border of the camera view.
int xOffset = (mOpenCvCameraView.getWidth() - cols) / 2; int yOffset = (mOpenCvCameraView.getHeight() - rows) / 2; mOpenCvCameraView.getWidth() gives the number of rows in the entire camera view, i.e. the camera frame PLUS the black border of the camera view around the frame. (mOpenCvCameraView.getWidth() - cols) gives the sum of the width of left and right black border of the camera view. (mOpenCvCameraView.getWidth() - cols)/2 or xOffset gives the width of the black border on one side, i.e. either right or left side, black border of the camera view. Likewise for yOffset
int x = (int)event.getX() - xOffset; int y = (int)event.getY() - yOffset; getX() returns the X coordinate of this event for the first pointer index. So getX() gives the distance of the touched region from the left extreme side of the camera view, which includes the black border on the left. So event.getX()-xOffset or int x is the distance of the touched region from the left extreme side of the camera "frame" (which does NOT include the black border of the camera view). Likewise for int y.
Then are the lines which I have no clue about.

I assume you're asking about these lines:
touchedRect.x = (x>4) ? x-4 : 0;
touchedRect.y = (y>4) ? y-4 : 0;
touchedRect.width = (x+4 < cols) ? x + 4 - touchedRect.x : cols - touchedRect.x;
touchedRect.height = (y+4 < rows) ? y + 4 - touchedRect.y : rows - touchedRect.y;
From what I can tell, it's basically just making sure that Rectangle height and width don't wander out of frame.
Breakdown of why -
The ? operator (called the ternary operator) in Java is basically a shorthand of if/else - the line touchedRect.x = (x>4) ? x-4 : 0 means
if (x>4) {
touchedRect.x = x-4;
} else {
touchedRect.x = 0;
}
So, for line 129, if x > 4, set touchedRect.x to x-4, else 0.
line 130, if y > 4, set touchedRect.y to y-4, else 0.
line 131, touchedRect.width becomes x+4 - touchedRect.x if x+4 < cols, else touchedRect.width becomes cols - touchedRect.x
line 132, touchedRect.height becomes y+4-touchedRect.y if y+4 < rows, else it becomes rows - touchedRect.y

Related

I want to draw multiple rectangle using loops in android

I want to take user input in numbers and based on that input I want to draw multiple numbers of rectangles on screen using for loop.
This is the image that I want to implement.
thank you in advance.
There are several ways to do this. Probably the simplest way is to draw rects to a canvas using 2 nested for loops, like so:
for (int n = 0; n < numRows; n++){
for (int m = 0; m < numColumns; m++){
canvas.drawRect(leftMargin + m*columnWidth, topMargin + n*rowHeight,
leftMargin + m*columnWidth + width, topMargin + n*rowHeight + height, myPaint);
}
}
where:
numRows = # of rows to draw
numColumns = # of columns to draw
leftmargin = distance from left side of screen to first column
topMargin = distance from top of screen to first row
columnWidth = width of each column of rects
rowHeight = heigth of each row of rects
width = width of each rect, if equal to columnWidth there will be no space between rects.
height = height of each rect, if equal to rowHeight there will be no space between.
myPaint = the Paint object you define for your rects, where you would specify things like color, filled in or outline, opacity, etc.

Android grid of dots to fill View

I'm trying to make a game in Android Studio using Kotlin, currently I have a grid drawn using two for loops (1 for cols and 1 for rows) that draws dots. I want the dots to fill the View, without the blank space around the grid. Any ideas?
val columns = 5
val rows = 5
var xPos: Float = width / (columns + 1)
var yPos: Float = height / (rows + 1)
for (col in 1..columns) {
for (row in 1..rows) {
canvas.drawPoint(col*xPos, row*yPos, paint)
}
}
What I get:
d
What I want:
You need a little space around the edges. So you can have a variable for padding, which should be half the diameter of your dots plus however many pixels of white you want around them. You would probably calculate the size using a constant DIP unit and the screen density, same as you probably did for your paint to get the dot diameter.
Then you can use the padding in your calculation. For example, if you have five columns, to get the space between dots, you want to divide the width by four, after subtracting the padding from both sides.
val padding = /* ... */
val columns = 5
val rows = 5
val hSpacing = (width - (2 * padding)) / (columns - 1)
val vSpacing = (height - (2 * padding)) / (rows - 1)
for (i in 0..columns)
for (j in 0..rows)
canvas.drawPoint(padding + i * hSpacing, padding + j * vSpacing, paint)

The algorithm rotation of YUV_420_888 format in react-native-camera

Below is the rotation code from react-native-camera to support ZXING library detect barcode. Specificed link is here
private byte[] rotateImage(byte[] imageData, int width, int height) {
byte[] rotated = new byte[imageData.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
rotated[x * height + height - y - 1] = imageData[x + y * width];
}
}
return rotated;
}
imageData is YUV_420_888 format
I know it's rotate a frame, but how it is really work? Is it rotate 90 or 180 degree? In clockwise or anticlockwise direction?
I'm struggle to test it with sample images I put in so completely dont understand it.
The code that you have posted rotates a 1 byte per pixel monochrome (grey-scale) image 90 degrees clockwise and returns it in a new byte array. It doesn't process any chroma information.
The YUV_420_888 image format stores an image in YUV format, where Y is the luma (grey-scale component) which is stored first in memory, and U and V are the chroma components which are stored after the luma. To save space, U and V are stored at half the horizontal and vertical resolution of the luma component.
Because the luma component is stored first, if you just ignore the chroma channels that come after it, you can treat it as a monochrome image, which is what the code is doing.
To do the actual rotation, the code is iterating over all the pixels in y and x. For each pixel, it calculates the new pixel location in the rotated image and copies it there.
Here is a badly-drawn diagram of what's happening:
The YUV_420_888 stores the pixels one row at a time, top-to-bottom, left-to-right. So the math to compute a pixel location is like this:
old_pixel_location = (y * width) + x
As you can see in the image, the old image width becomes the new image height and vice versa. The pixel position in the rotated image has a new_y value equal to the x value, and a new_x value which is y pixels to the left of the right side of the image.
new_width = height
new_height = width
new_x = (new_width - 1) - y
new_y = x
The new pixel position is:
new_pixel_location = (new_y * new_width) + new_x
// substituting gives:
new_x = (height - 1) - y
new_pixel_location = (x * height) + ((height - 1) - y)
// removing brackets and re-ordering:
old_pixel_location = x + y * width
new_pixel_location = x * height + height - y - 1

How to find the center point and border of the image inside an ImageView

There is an ImageView and it has an Image, which has been zoomed and rotated.
See the picture:
The image is scaled by android.graphics.Matrix.
You can see there is red point which is the center point of the image, and also a blue border. How to calculate them?
(Update: I want to operate on the image of the girl inside the blue border, not the whole picture, that's background)
There is a mathematical dependency between two. If there is other information available:
-assuming rectangle has right angles for all corners-.
center.X = (aCorner.X + oppositeCorner.X)/2;
center.Y = (aCorner.Y + oppositeCorner.Y)/2;
Where aCorner is a arbitrary corner and oppositeCorner is opposite corner to aCorner.
This was trivial, a little more hard work included to calculate borders (and a bit more of information; center position, width and the height of the picture and rotation angle).
Assuming image's width is "w", height is "h", angle is "a", and center "cX" and "cY".
First corner;
length = sqrt(w^2+h^2)/2;
x = (length)*(cos(a)*(-w/length) - (h/length)*sin(a)) + cX;
y = (length)*(sin(a)*(-w/length) + (h/length)*cos(a)) + cY;
Second corner;
x = (length)*(cos(a)*(w/length) + sin(a)*(h/length)) + cX;
y = (length)*(cos(a)*(h/length) - sin(a)*(w/length)) + cY;
Third;
x = -(length)*(cos(a)*(-w/length) + (h/length)*sin(a)) + cX;
y = -(length)*(sin(a)*(-w/length) - (h/length)*cos(a)) + cY;
Fourth;
x = -(length)*(cos(a)*(w/length) - sin(a)*(h/length)) + cX;
y = (length)*(cos(a)*(h/length) - sin(a)*(w/length)) + cY;
Length is a half of diagonal of the rectangle. The inner part with cos and sin is result of trigonometric transformation:
sin(a+b) = sin(a)*cos(b) + cos(a)*sin(b)
[....]
And cX and cY is used to translate corners from a arbitrary coordinate system to a specific coordinate system.
I know, I know this was kind of overkill. Matrix class may have this functions on its own. I believe if it has, the method used in it can be broken into method I described here.
NOTE: Angle a -actually even sin(a) and cos(a), which is better- can be accessed via
Matrix.getValues(float[] values)
Most 2D matrices use this scheme:
| sin(a) 0 0 |
| 0 -cos(a) 0 |
| 0 0 scale|
I am not sure about particular implementation of Android API.
BTW, there may have been some signature errors up there so be careful.

can someone please explain the last two params of arcTo?

I'm trying to draw a rectangle with rounded corners. I have a javascript path that does this, but the javascript arcTo method takes a rectangle (to define its oval) and then one param which sets the sweep.
However, in the Android version there are three params. the rectangle oval (which I think I have defined correctly) and then the startAngle and sweepAngle (which I'm not understanding the usage of), but my arcs don't look anything like what I'm expecting when I noodle with how I'm guessing they should work.
Does anyone know of a good tutorial on this?
Specifically I'm trying to understand what would the two params look like if I was trying to draw an arc (on a clock face) from 12 - 3, and then assuming I had a line that ran down from the 3 and then needed to round the corner from 3 to 6 and so forth.
Here's my code (disregard the arc numbers in there now... that's just the latest iteration of my guessing at how this may work, having failed on the previous, more sensible attempts):
Path ctx = new Path();
ctx.moveTo(X+5,Y); //A
ctx.lineTo(X+W-5,Y);//B
ctx.arcTo(new RectF(X+W, Y, X+W, Y+5), -180, 90); //B arc
ctx.lineTo(X+W,Y+H-5); //C
ctx.arcTo(new RectF(X+W,Y+H,X+W-5,Y+H),90,180); //C arc
ctx.lineTo(X+W/2 +6,Y+H);
ctx.lineTo(X+W/2,Y+H+8);
ctx.lineTo(X+W/2-6,Y+H);
ctx.lineTo(X+5,Y+H);
ctx.arcTo(new RectF(X,Y+H,X,Y+H-5),180,270);
ctx.lineTo(X,Y+5);
ctx.arcTo(new RectF(X,Y,X+5,Y),270,0);
Paint p = new Paint();
p.setColor(0xffff00ff);
canvas.drawPath(ctx, p);
much obliged.
odd that no one piped in with an answer, once I found it (it wasn't easy to find) it was really straight forward.
So, the way it works is this:
Assuming you want to draw a rounded corner at 12 - 3 (using clock reference):
you start your path and when you need the line to arc you define a rectangle whose upper left corner is the place where your line is currently terminated and whose lower right corner is the place that you want the arc to go to, so if you imagine a square whose X,Y is 12 (on the clock) and whose X+W,Y+H is 3 that's the square you need.
Now, imagine that you have an oval in that square (in this example it's a circular oval, if you want your curve to be more oval-ish, then define your square as a rectangle), you can take any slice of that circle using the last two params of the method. The first param defines the angle where you want to start cutting. If we're using a compass, 0 degrees is East (not sure why, I'm not a geometry expert... is this normal? I always think of 0 being North, but all the programming geometry examples I see have 0 as East, maybe someone will comment on why that is).
The second param defines how much of the circle you want. If you want the whole circle you put 360 if you want half the circle you put 180 etc.
So, in our case since we want to round the corner from 12 to 3, we put 270 as our starting degree and grab 90 degrees of the circle.
Lastly, when you're done with this process, the line now thinks of itself as being at 3pm so you can continue lineTo(ing) from there.
So... here's my fixed code for my shape (it has a little triangle in it, but that's neither here nor there, the actual rounded parts are B-C, D-E, I-J, and K-A. All the rest are straight lines.
int arc = 25;
public Cursor(int X, int Y, int W, int H){
/*
* A B
* K C
* J D
* I H F E
G
*/
int Ax = X+ arc;
int Ay = Y;
int Bx = X + W - arc;
int By = Y;
int Cx = X + W;
int Cy = Y + arc;
int Dx = Cx;
int Dy = (Y + arc) + (H - arc*2);
int Ex = Bx;
int Ey = Y + H;
int Fx = X+W/2 +6;
int Fy = Ey;
int Gx = X+W/2;
int Gy = Y+H+8;
int Hx = X+W/2-6;
int Hy = Ey;
int Ix = Ax;
int Iy = Hy;
int Jx = X;
int Jy = Dy;
int Kx = X;
int Ky = Cy;
Path ctx = new Path();
ctx.moveTo(Ax,Ay); //A
ctx.lineTo(Bx,By);//B
ctx.arcTo(new RectF(Bx, By, Cx, Cy), 270, 90); //B-C arc
ctx.lineTo(Dx,Dy); //D
ctx.arcTo(new RectF(Dx - arc, Dy, Ex + arc, Ey),0,90); //D-E arc
ctx.lineTo(Fx, Fy); //E-F
ctx.lineTo(Gx, Gy); //F-G
ctx.lineTo(Hx, Hy); //G-H
ctx.lineTo(Ix, Iy); //H - I
ctx.arcTo(new RectF(Jx, Jy, Ix, Iy),90,90);// I = J arc
ctx.lineTo(Kx, Ky); //K
ctx.arcTo(new RectF(Ax - arc, Ay, Kx + arc, Ky),180,90); //K - A arc
ctx.lineTo(Ax, Ay); //K
Paint p = new Paint();
p.setAntiAlias(true);
p.setColor(0xffffffff);
p.setStyle(Style.FILL);
canvas.drawPath(ctx, p);
p.setColor(0xff000000);
p.setStyle(Style.STROKE);
p.setStrokeWidth(3);
canvas.drawPath(ctx, p);
}
This answer visually explains all arcTo parameters using four examples.
arcTo takes the following parameters:
public void arcTo(RectF oval,
float startAngle,
float sweepAngle,
boolean forceMoveTo)
where RectF's constructor takes:
RectF(float left, float top, float right, float bottom)
(Hopefully this visualization is less painful and less mystifying than reading the official arcTo documentation.)
Thanks for this example, it makes the parameters very clear to understand.
From what I read in the dev docs of Android you can probably spare yourself some of the "lineTo()" calls (except those to points F,G,H), since arcTo automatically adds a lineTo when the first point of the arc is not the last point drawn...
As for why 0 starts East, it is so because of math and trigonometry lessons generally assume that the 0 degrees mark is the point where the trigonometric circle (circle with center 0,0 and radius 1) intersects with the X-axis, which is East (these same lessons however generally count the angles counter-clockwise, so 90 degrees becomes north and 270 is south, whereas on Android it seems the angles are counted clockwise)
Here's some sample code (pieced together from one of my classes) to draw a filled, rounded corner rectangle and then adding a stroked rectangle to give it a border:
//Initializing some stuff
_paint = new Paint();
_rect = new RectF();
_radius = 10;
_bgColor = 0xFFFFFFFF;
_borderColor = 0xFFCCCCCC;
//Doing dimension calculations
_rect.left = 0;
_rect.top = 0;
_rect.right = this.getWidth() - 1;
_rect.bottom = this.getHeight() - 1;
//painting
//draw the background
_paint.setColor(_bgColor);
_paint.setStyle(Style.FILL_AND_STROKE);
canvas.drawRoundRect(_rect, _radius, _radius, _paint);
//draw the border
_paint.setStrokeWidth(1);
_paint.setColor(_borderColor);
_paint.setStyle(Style.STROKE);
canvas.drawRoundRect(_rect, _radius, _radius, _paint);

Categories

Resources