How can the landmarks for hands be accessed in the Android version of MediaPipe? (Java)
I'd like to access the joints' positions in space.
A similar project that someone else has done in Python is available.
https://github.com/GasbaouiMohammedAlAmin/Finger-Counter-using-Hand-Tracking-And-Open-cv
The landmarks seem to be from the "solutions" part of the library.
I have a sample of code that works for Android, but it seems that all the processing is done without ever giving the landmarks to the Java file. https://github.com/jiuqiant/mediapipe_multi_hands_tracking_aar_example Is the base code of my project.
From that file, getMultiHandLandmarksDebugString takes in NormalizedLandmarkList, but how can those landmarks be accessed normally in the program?
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmark;
Here the result is the object of HandsResults
To get the landmark position of each points i have used a list of normalizedlandmark type
List<NormalizedLandmark> ll = result.multiHandLandmarks().get(0).getLandmarkList();
The list size will be 21 to access those i have created a loop
for (int i = 0; i < ll.size(); i++)
For getting x, y and z of each of 21 landmarks
int x = (int) (ll.get(i).getX() * 1280);
Multiplied by 1280 texture width to get the pixel value
Here is the sample code u can check it out for more reference follow this link
List<NormalizedLandmark> ll = result.multiHandLandmarks().get(0).getLandmarkList();
Log.d("check", String.valueOf(ll.get(0).getX()));
for (int i = 0; i < ll.size(); i++) {
int x = (int) (ll.get(i).getX() * 1280);
int y = (int) (ll.get(i).getY() * 720);
// Log.d("chec1", String.valueOf(i)+" "+String.valueOf(x)+" "+String.valueOf(y));
for (int tip : finger_tips) {
Log.d("chec1", String.valueOf(tip) + " " + String.valueOf((int) (ll.get(tip).getX() * 1280)) + " " + String.valueOf((int) (ll.get(tip).getY() * 720)));
if ((ll.get(tip).getX() * 1280) < (ll.get(tip - 3).getX() * 1280)) {
finger_fold_status.add(false);
} else {
finger_fold_status.add(true);
}
}
int r = (int) (ll.get(thumb_tip-1).getY()*1280);
if(((int)ll.get(thumb_tip).getY()*1280)<r && r< (ll.get(thumb_tip-2).getY()*1280)){
if(finger_fold_status.contains(true)){
Log.d("res","LIke");
is = true;
runOnUiThread(new Runnable() {
#Override
public void run() {
tv.setText(R.string.like);
}
});
}
}if(((int)ll.get(thumb_tip).getY()*1280)>r && r>(ll.get(thumb_tip-2).getY()*1280)){
if(finger_fold_status.contains(true)){
Log.d("res1","DisLIke");
is = false;
runOnUiThread(new Runnable() {
#Override
public void run() {
tv.setText(R.string.dislike);
}
});
}
}
Log.d("stat", String.valueOf(finger_fold_status));
}
Related
I want to convert an image from Android camera to HSI format using OpenCV.
The problem is when I use the following method
private Mat rgb2hsi(Mat rgbFrame) {
Mat hsiFrame = rgbFrame.clone();
for( int i = 0; i < rgbFrame.rows(); ++i ) {
for( int j = 0; j < rgbFrame.cols(); ++j ) {
double[] rgb = rgbFrame.get(i, j);
Log.d(MAINTAG, "rgbFrame.get(i, j) array size = " + rgb.length);
double colorR = rgb[0];
double colorG = rgb[1];
double colorB = rgb[2];
double minRGB = min(colorR, colorG, colorB);
double colorI = (colorR + colorG + colorB) / 3;
double colorS = 0.0;
if(colorI > 0) colorS = 1.0 - (minRGB / colorI);
double colorH;
double const1 = colorR - (colorG / 2) - (colorB / 2);
double const2 = Math.sqrt(Math.pow(colorR, 2) + Math.pow(colorG, 2) + Math.pow(colorR, 2)
- (colorR * colorG) - (colorR * colorB) - (colorG * colorB));
colorH = Math.acos(const1 / const2);
if(colorB > colorG) colorH = 360 - colorH;
double[] hsi = {colorH, colorS, colorI};
hsiFrame.put(i, j, hsi);
}
}
return hsiFrame;
}
It shows an error
java.lang.UnsupportedOperationException: Provided data element number (3) should be multiple of the Mat channels count (4)
I search for a while to figure out the cause of this error.
I found that I put an array of size 3 instead of 4.
Android convert byte array from Camera API to color Mat object openCV
I wonder what Type of image receive from Android Camera.
Why when I get an array of size 4?
How to convert an image received from Android camera to HSI and preview on the screen?
The following is the overrided method onCameraFrame
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Mat outputFrame = inputFrame.rgba();
/* Get RGB color from the pixel at [index_row, index_column] */
int index_row = 0;
int index_column = 0;
final double[] mRgb_pixel = outputFrame.get(index_row, index_column);
/* Show the result */
runOnUiThread(new Runnable() {
#Override
public void run() {
int r = (int) mRgb_pixel[0];
int g = (int) mRgb_pixel[1];
int b = (int) mRgb_pixel[2];
/* Set RGB color */
mRred_textview.setText("Red\n" + Double.toString(mRgb_pixel[0]));
mGreen_textview.setText("Green\n" + Double.toString(mRgb_pixel[1]));
mBlue_textview.setText("Blue\n" + Double.toString(mRgb_pixel[2]));
mColor_textview.setBackgroundColor(Color.rgb(r, g, b));
}
});
if(mPreviewType == PreviewType.GB) {
outputFrame.convertTo(outputFrame, CvType.CV_64FC3);
return getGBColor(rgb2hsi(outputFrame));
} else if (mPreviewType == PreviewType.HSI) {
outputFrame.convertTo(outputFrame, CvType.CV_64FC3);
return rgb2hsi(outputFrame);
} else {
return outputFrame;
}
}
My MainActivity implements CameraBridgeViewBase.CvCameraViewListener2
[Edit]
I think that the reason why it return an array of size 4 is because the frame is in RGBA format, not RGB format.
Therefore, how to convert RGBA to HSI and preview the frame on the screen?
The problem here is that your hsiFrame is a 4 channel image and your hsi array has only 3 values. You need to add one term corresponding to alpha channel to your hsi array. Making either of the following changes should work for you:
1. double[] hsi = {colorH, colorS, colorI, rgb[3]};
2. Mat hsiFrame = new Mat(rgbFrame.size(), CvType.CV_8UC3);
Hope this helps.
I use PDFJet-Open-Source library to construct a pdf. So, I have couple of questions:
1) How can I place the multiline text inside Cell?
Problem description: Currently I faced with problem that I can't place the multiline text inside Cell object. I tried set text like "text1 \n text2..." but it does not have any effect. Unfortunatelly, open source version does not have TextColumn and Paragraph classes.
2) What is the CompositeTextLine and how to use it?
Problem description: Perhaps I have wrong imagination, but I tried to do the following:
...
CompositeTextLine ctl = new CompositTextLine(0,0);
ctl.addComponent(new TextLine(f1,"MyText1"));
ctl.addComponent(new TextLine(f1,"MyText2"));
ctl.addComponent(new TextLine(f1,"MyText3"));
Cell cell = new Cell(f1);
cell.setCompositeTextLine(ctl);
...
I expected to see several multiple lines in the Cell but I observed nothing. Moreover, if I add the line table.wrapAroundCellText(), I've got NullPointerException. If I call ctl.drawOn(page), I just observe: "MyText1 MyText2 MyText3" without line breaking.
UPDATE: I discovered the TextBox class, so that, if I write:
TextBox textbox = new TextBox(f1);
textbox.setText("First Line \n Second Line");
textbox.drawOn(page);
it will construct what I want:
First Line
Second Line
But still I am interested with the possibility of (1) and description of (2) and some of them variations, like to "How to set TextBox (or image etc.) inside Cell, not only single line?"
And last one, could anyone, please, refer me to the realization of "text justification" algorithm in Java or C++.
I can across the same problem, and I ended up extending Cell, and using WordUtils from apache commons lang3:
public class MultilineCell extends Cell {
private final int characterCount;
public MultilineCell(Font font, String content, int characterCount) {
super(font, content);
this.characterCount = characterCount;
}
#Override
public String getText() {
return WrapUtil.wrap(super.getText(), this.characterCount);
}
#Override
public float getHeight() {
float height = this.font.getBodyHeight();
String text = getText();
if (text != null) {
String[] wrappedTexts = text.split(System.getProperty("line.separator"));
if (wrappedTexts.length > 1) {
return (height * wrappedTexts.length) + this.top_padding + this.bottom_padding;
}
}
return height + this.top_padding + this.bottom_padding;
}
#Override
protected void paint(Page page, float x, float y, float w, float h) throws Exception {
page.setPenColor(this.getPenColor());
page.setPenWidth(this.lineWidth);
drawBorders(page, x, y, w, h);
drawText(page, x, y, w);
}
private void drawBorders(
Page page,
float x,
float y,
float cell_w,
float cell_h) throws Exception {
if (getBorder(Border.TOP) &&
getBorder(Border.BOTTOM) &&
getBorder(Border.LEFT) &&
getBorder(Border.RIGHT)) {
page.drawRect(x, y, cell_w, cell_h);
}
else {
if (getBorder(Border.TOP)) {
page.moveTo(x, y);
page.lineTo(x + cell_w, y);
page.strokePath();
}
if (getBorder(Border.BOTTOM)) {
page.moveTo(x, y + cell_h);
page.lineTo(x + cell_w, y + cell_h);
page.strokePath();
}
if (getBorder(Border.LEFT)) {
page.moveTo(x, y);
page.lineTo(x, y + cell_h);
page.strokePath();
}
if (getBorder(Border.RIGHT)) {
page.moveTo(x + cell_w, y);
page.lineTo(x + cell_w, y + cell_h);
page.strokePath();
}
}
}
private void drawText(
Page page,
float x,
float y,
float cell_w) throws IOException {
String wrappedText = WrapUtil.wrap(super.getText(), this.characterCount);
String[] lines = wrappedText.split(System.getProperty("line.separator"));
float x_text = x + this.left_padding;
float y_text = y + this.font.getAscent() + this.top_padding;
for (String line : lines) {
page.drawString(this.font, line, x_text, y_text);
y_text += this.font.getBodyHeight();
}
}
}
You can instantiate and add the MultilineCell as you would with a Cell:
List<Cell> rowCells = new ArrayList<Cell>();
rowCells.add(new MultilineCell(font, c.getString(reasonIdx), 42));
I know that extension is not a nice solution, and copying drawBorders() is even worse, but in this case, it is the only solution if you don't want to fork PDFJet.
This however breaks autoAdjustColumnWidths: the width is calculated on the whole text, instead of the longest line. So if you intend to use this method, either subclass Table, fork PDFJet, or extract just this method (the only downside of the latter is that I couldn't work around the cell padding though):
/**
* Auto adjusts the widths of all columns so that they are just wide enough to hold the text without truncation.
*/
private static void autoAdjustColumnWidths(List<List<Cell>> tableData) {
// Find the maximum text width for each column
float[] max_col_widths = new float[tableData.get(0).size()];
for (int i = 0; i < tableData.size(); i++) {
List<Cell> row = tableData.get(i);
for (int j = 0; j < row.size(); j++) {
Cell cell = row.get(j);
if (cell.getColSpan() == 1) {
float cellWidth = 0f;
if (cell.getImage() != null) {
cellWidth = cell.getImage().getWidth();
}
if (cell.getText() != null) {
// Is this a multiline cell? If so, measure the widest line
if (cell.getText().contains(MultilineCell.NEW_LINE)) {
String[] lines = cell.getText().split(MultilineCell.NEW_LINE);
for (String line : lines) {
if (cell.getFont().stringWidth(cell.getFallbackFont(), line) > cellWidth) {
cellWidth = cell.getFont().stringWidth(cell.getFallbackFont(), line);
}
}
}
// Standard (single-line) cell, measure whole text
else {
if (cell.getFont().stringWidth(cell.getFallbackFont(), cell.getText()) > cellWidth) {
cellWidth = cell.getFont().stringWidth(cell.getFallbackFont(), cell.getText());
}
}
}
cell.setWidth(cellWidth + 2f /*cell.left_padding*/ + 2f/*cell.right_padding*/);
if (max_col_widths[j] == 0f ||
cell.getWidth() > max_col_widths[j]) {
max_col_widths[j] = cell.getWidth();
}
}
}
}
for (int i = 0; i < tableData.size(); i++) {
List<Cell> row = tableData.get(i);
for (int j = 0; j < row.size(); j++) {
Cell cell = row.get(j);
cell.setWidth(max_col_widths[j]);
}
}
}
PDFJet is a funny library, btw: protected fields, every other method throws Exception, and the classes are definitely not designed for extension, even if there're not final.
There is a tricky solution for that:
As suggested before split your input into several lines (using for example using WordUtils from apache commons lang3)
String wrappedText = WrapUtil.wrap(super.getText(), this.characterCount);
String[] lines = wrappedText.split(System.getProperty("line.separator"));
After you have your lines, add as many rows as lines into your table with those separate lines.
To simulate "multiline cell" call function removeLineBetweenRows providing index of those added rows. It will look like a one big cell.
I want to get the dominant color in an Android CvCameraViewFrame object. I use the following OpenCV Android code to do that. This code is converted from OpenCV c++ code to OpenCV Android code. In the following code I loop through all the pixels in my camera frame and find the color of each pixel and store them in a HashMap to find the dominant color at the end of the loop. To loop through each pixel it takes about 30 seconds. This is unacceptable for me. Could somebody please review this code and point me how can I find the dominant color in a camera frame.
private String[] colors = {"cBLACK", "cWHITE", "cGREY", "cRED", "cORANGE", "cYELLOW", "cGREEN", "cAQUA", "cBLUE", "cPURPLE", "cPINK", "cRED"};
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
if (mIsColorSelected) {
Imgproc.cvtColor(mRgba, mRgba, Imgproc.COLOR_BGR2HSV);
int h = mRgba.height(); // Pixel height
int w = mRgba.width(); // Pixel width
int rowSize = (int)mRgba.step1(); // Size of row in bytes, including extra padding
float initialConfidence = 1.0f;
Map<String, Integer> tallyColors = new HashMap<String, Integer>();
byte[] pixelsTotal = new byte[h*rowSize];
mRgba.get(0,0,pixelsTotal);
//This for loop takes about 30 seconds to process for my camera frame
for (int y=0; y<h; y++) {
for (int x=0; x<w; x++) {
// Get the HSV pixel components
int hVal = (int)pixelsTotal[(y*rowSize) + x + 0]; // Hue
int sVal = (int)pixelsTotal[(y*rowSize) + x + 1]; // Saturation
int vVal = (int)pixelsTotal[(y*rowSize) + x + 2]; // Value (Brightness)
// Determine what type of color the HSV pixel is.
String ctype = getPixelColorType(hVal, sVal, vVal);
// Keep count of these colors.
int totalNum = 0;
try{
totalNum = tallyColors.get(ctype);
} catch(Exception ex){
totalNum = 0;
}
totalNum++;
tallyColors.put(ctype, totalNum);
}
}
int tallyMaxIndex = 0;
int tallyMaxCount = -1;
int pixels = w * h;
for (int i=0; i<colors.length; i++) {
String v = colors[i];
int pixCount;
try{
pixCount = tallyColors.get(v);
} catch(Exception e){
pixCount = 0;
}
Log.i(TAG, v + " - " + (pixCount*100/pixels) + "%, ");
if (pixCount > tallyMaxCount) {
tallyMaxCount = pixCount;
tallyMaxIndex = i;
}
}
float percentage = initialConfidence * (tallyMaxCount * 100 / pixels);
Log.i(TAG, "Color of currency note: " + colors[tallyMaxIndex] + " (" + percentage + "% confidence).");
}
return mRgba;
}
private String getPixelColorType(int H, int S, int V)
{
String color;
if (V < 75)
color = "cBLACK";
else if (V > 190 && S < 27)
color = "cWHITE";
else if (S < 53 && V < 185)
color = "cGREY";
else { // Is a color
if (H < 14)
color = "cRED";
else if (H < 25)
color = "cORANGE";
else if (H < 34)
color = "cYELLOW";
else if (H < 73)
color = "cGREEN";
else if (H < 102)
color = "cAQUA";
else if (H < 127)
color = "cBLUE";
else if (H < 149)
color = "cPURPLE";
else if (H < 175)
color = "cPINK";
else // full circle
color = "cRED"; // back to Red
}
return color;
}
Thank you very much.
OpenCV has an Histogram method which counts all image colors. After the histogram is calculated all you would have to do is to chose the one with the biggest count...
Check here for a tutorial (C++): Histogram Calculation.
You might also the this stackoverflow answer which shows an example on how to use Android's histogram function Imgproc.calcHist().
Think about to resize your images, then you may multiply the results by the same scale:
resize( larg_image, smallerImage , interpolation=cv.CV_INTER_CUBIC );
Or,
you may check these solutions:
You could find dominant color using k-mean clustering method.
this link will be useful.
https://www.youtube.com/watch?v=f54-x3PckH8
I am working to convert my CCScrollLayer.cpp code into more of a CCPicker code. In CCScrollLayer, I've defined a baselayer (CCColorLayer) to hold all of the menu objects. I also have a border that holds the ScrollLayer (using glScissor) called "overlay" that I want to be completely static.
In Objective C:
...
self.baseLayer = [CCLayerColor layerWithColor:(ccColor4B){150,150,150,0} width:s.width height:imgSize.height * numPages];
for (int i=0; i < [arrayPages count]; i++) {
CCNode* n = arrayPages[i];
n.position = ccp(s.width/2, s.height/2 + i * (imgSize.height + padding));
[baselayer addChild:n];
}
baseLayer.position = ccp(-s.width/2, -s.height/2 - s.height * currentPage);
[self addChild:baselayer];
...
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint n = [self convertTouchToNodeSpace:touch];
baselayer.position = ccp(touchStartedbaseLayerPosition.x, touchStartedbaseLayerPosition.y + n.y - touchStartedPoint.y);
}
In C++
...
overlay=new CCSprite();
overlay->initWithFile("overlay.png");
overlay->setPosition(ccp(300,300));
overlay->autorelease();
CCLayerColor* baselayer = new CCLayerColor();
baselayer->initWithColor(ccc4(255, 255, 255,255));
baselayer->setOpacity(255);
baselayer->setContentSize(CCSizeMake(s.width/10, (layers->count()*scrollWidth)));
baselayer->setPosition(s.width*.41,300);
baselayer->autorelease();
// Loop through the array and add the screens
unsigned int i;
for (i=0; i<layers->count(); i++)
{
CCLayer* l = static_cast<CCLayer*>(layers->objectAtIndex(i));
l->setAnchorPoint(ccp(0,0));
l->setPosition(ccp(s.width/40,(i*scrollWidth)));
baselayer->addChild(l);
}
this->addChild(baselayer,1);
this->addChild(overlay,1);
...
void CCScrollLayer::ccTouchMoved(CCTouch *touch, CCEvent *withEvent)
{
CCPoint touchPoint = touch->getLocation(); // Get the touch position
touchPoint = this->getParent()->convertToNodeSpace(touchPoint);
baselayer->setPosition(ccp(0,(-(currentScreen-1)*scrollWidth)+(touchPoint.y-startSwipe)));
}
I'm not sure what I'm doing wrong, but as soon as I try to start scrolling, I get a fatal error in the touchdispatcher
case CCTOUCHMOVED:
pHandler->getDelegate()->ccTouchMoved(pTouch, pEvent);
I'm guessing I'm missing something simple here. Can you point me in the write direction?
i want to build a component that will be able to show a integer number max 5 digits in the style of the old analog car counters and animate the digit change.
that looks something like this maybe...
i have tried searching for this kinda of examples but i couldn't find anything so far.
in your opinion what is the best approach to achieve this?
i looked at the iphone alarm time picker and as far as i can tell there is only a fixed background and they push the numbers in or out the view. but how do i place the digits in this case and reference them to a particular value?
tnx.
You can try to create your own view, extending view and overriding onDraw().
Here you can use rows of numbers in bitmaps and editing their position based on the number you wish to show.
Dont forget to call invalidate() after setting new numbers to redraw the view.
I will paste an example containing a start for your project.
The bitmap number is a vertical image with numbers from 1-9 (and 0&.)
Ex.
class TickerView extends View { ..
public void setDouble(double d) {
value = d;
invalidate();
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int startx = 0;
int starty = 0;
DecimalFormat df = new DecimalFormat("#0.00");
String str = df.format(value);
String original = Double.toString(value);
Bitmap nums = BitmapFactory.decodeResource(context.getResources(),
R.drawable.numbers);
for (int i = 0; i < str.length(); i++) {
int num = 0;
try {
num = Integer.parseInt(str.charAt(i) + "");
} catch (Exception e) {
num = 10;
}
int numbefore = 0;
try {
numbefore = Integer.parseInt(original.charAt(i -1) + "");
} catch (Exception e) {
numbefore = 0;
}
canvas.drawBitmap(nums, startx + (i * 40), (starty + 40)
- (num * 50) + (numbefore), paintY);
}
paintY.setStrokeWidth(10);
canvas.drawLine(startx, starty+36, startx + (str.length() * 40), starty+36,
paintY);
canvas.drawLine(startx, starty + 90, startx + (str.length() * 40),
starty + 90, paintY);
invalidate();
}
}