[关闭]
@zimenglan 2015-03-27T08:53:59.000000Z 字数 16525 阅读 1943

human pose/joints annotation

human.pose annotation body.joints



使用说明

  1. 拷贝源码, 把它放到一个新建的jave project里面
  2. 运行(或者ctrl + F11), 会弹出一个框
  3. 将要标注的imageset扔到这个框里面去
  4. 剩下的就是开始标注图像了
  5. 需要注意的是, 在标完第一张的时候, 稍微等一两秒, 因为要生成对应的保存目录
  6. 源码只是在windows下测试过rgb图像和深度图

functions 介绍


保存格式


example

暂时不能从本地上传图片, 所有给不了标注时的效果图


code

  1. import java.awt.BasicStroke;
  2. import java.awt.Color;
  3. import java.awt.Graphics;
  4. import java.awt.Graphics2D;
  5. import java.awt.Image;
  6. import java.awt.datatransfer.DataFlavor;
  7. import java.awt.dnd.DnDConstants;
  8. import java.awt.dnd.DropTarget;
  9. import java.awt.dnd.DropTargetDragEvent;
  10. import java.awt.dnd.DropTargetDropEvent;
  11. import java.awt.dnd.DropTargetEvent;
  12. import java.awt.dnd.DropTargetListener;
  13. import java.awt.event.KeyEvent;
  14. import java.awt.event.KeyListener;
  15. import java.awt.event.MouseAdapter;
  16. import java.awt.event.MouseEvent;
  17. import java.awt.event.MouseMotionAdapter;
  18. import java.io.BufferedWriter;
  19. import java.io.File;
  20. import java.io.FileWriter;
  21. import java.util.Arrays;
  22. import java.util.Iterator;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import javax.imageio.ImageIO;
  26. import javax.swing.JFrame;
  27. import javax.swing.JOptionPane;
  28. public class PoseAnno_BB_And_Joints extends JFrame implements DropTargetListener {
  29. private static final long serialVersionUID = 1L;
  30. // one bounding box, and 15 body joints
  31. private static String[] annoName = {
  32. // a bounding box for a whole person
  33. "human_bb_top_left_cornor",
  34. "human_bb_bottom_right_cornor",
  35. // the body joints
  36. "head", "neck", "torso",
  37. "left_shoulder", "left_elbow", "left_hand/wrist",
  38. "right_shoulder", "right_elbow", "right_hand/wrist",
  39. "left_hip", "left_knee", "left_foot/ankle",
  40. "right_hip", "right_knee", "right_foot/ankle"
  41. };
  42. //
  43. private static int[] annoNameIndice = {
  44. 0, 1, 2, 3, 4,
  45. 5, 6, 7, 8, 9, 10,
  46. 11, 12, 13, 14, 15
  47. };
  48. // if the occluded area of the body part is more that 30 percentage,
  49. // the body part is occluded
  50. private static String[] occState = {
  51. "non_occ", // default - 0
  52. "occ" // 1
  53. };
  54. private Image offScreenImage = null; // avoid flicker
  55. private Graphics gOffScreenGraphics;
  56. // handler of the annotated image
  57. private Image initImage = null;
  58. // path to the annotated dataset
  59. private String path = null;
  60. private String _prePath = null;
  61. private String _imagesetName = null;
  62. // the image name without extension
  63. private String fileName = null;
  64. private String fileName2 = null;
  65. private final String _labelExt = ".label";
  66. private final String _saveDirExt = "_AnnoPose";
  67. // image file list
  68. private LinkedList<File> file_list = new LinkedList<File>();
  69. // index of images
  70. private int curFileIdx = 0;
  71. // if shift is pressed, then draw
  72. // horizontal or
  73. // vertical line.
  74. private boolean shift_flag = false;
  75. // flag to occlusion state
  76. // 0: non-occluded, 1: occluded
  77. private int[] isOccluded = new int[annoName.length];
  78. // each part is annotated by one point
  79. // private static final int numXY = 2;
  80. // private int[][] xPoints = new int[annoName.length + 1][numXY];
  81. // private int[][] yPoints = new int[annoName.length + 1][numXY];
  82. // the first two points for human bounding box
  83. // the rest for 15 human body joints
  84. private final int totalLen = annoName.length;
  85. private int[] xPoints = new int[totalLen];
  86. private int[] yPoints = new int[totalLen];
  87. // since human_bb occupies two points
  88. private int[] points = new int[totalLen];
  89. // index of parts
  90. private int partsIdx = 0;
  91. // initialize some variables
  92. private static final double scale = 1;
  93. private static final int _width = 640;
  94. private static final int _height = 480;
  95. private static final int _startX = 480;
  96. private static final int _startY = 240;
  97. private static final int _border = 10;
  98. private static final int _startJframeX = 0;
  99. private static final int _startJframeY = 20;
  100. private static final int _circle_len = 5;
  101. private static final String[] moustType = {
  102. "left mouse button",
  103. "middle mouse wheel",
  104. "right mouse button"
  105. };
  106. // ***************************************************************
  107. public PoseAnno_BB_And_Joints() {
  108. //
  109. setTitle("Human Pose Annotation");
  110. setSize(_width, _height);
  111. setLocation(_startX, _startY);
  112. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  113. // button 1: the left mouse button
  114. // button 2: the mouse wheel
  115. // button 3: the right mouse button
  116. addMouseListener(new MouseAdapter() {
  117. @Override
  118. public void mouseClicked(MouseEvent e) {
  119. int type = e.getButton();
  120. System.out.println("mouse click type: " + moustType[type - 1]);
  121. System.out.println("partsIdx: " + partsIdx);
  122. if (initImage == null || partsIdx == totalLen || type == MouseEvent.BUTTON2) {
  123. return;
  124. }
  125. if (type == MouseEvent.BUTTON3) {
  126. xPoints[partsIdx] = 0;
  127. yPoints[partsIdx] = 0;
  128. // need to be reset
  129. if (partsIdx < 0) {
  130. Arrays.fill(isOccluded, 0);
  131. Arrays.fill(xPoints, 0);
  132. Arrays.fill(yPoints, 0);
  133. partsIdx = 0;
  134. }
  135. PoseAnno_BB_And_Joints.this.repaint();
  136. return;
  137. }
  138. // get the point location
  139. xPoints[partsIdx] = e.getX() - _startJframeX;
  140. yPoints[partsIdx] = e.getY() - _startJframeY;
  141. // System.out.println("mouse click point: (" +
  142. // e.getX() + ", " + e.getY() + ")");
  143. System.out.println("mouse click point: (" + xPoints[partsIdx] +
  144. ", " + yPoints[partsIdx] + ")");
  145. if (xPoints[partsIdx] < _border) {
  146. xPoints[partsIdx] = _border;
  147. }
  148. if (xPoints[partsIdx] >= initImage.getWidth(null) + _border) {
  149. xPoints[partsIdx] = initImage.getWidth(null) + _border - 1;
  150. }
  151. if (yPoints[partsIdx] < _border) {
  152. yPoints[partsIdx] = _border;
  153. }
  154. if (yPoints[partsIdx] >= initImage.getHeight(null) + _border) {
  155. yPoints[partsIdx] = initImage.getHeight(null) + _border - 1;
  156. }
  157. // isOccluded[partsIdx] = 0;
  158. //
  159. partsIdx++;
  160. // repaint
  161. PoseAnno_BB_And_Joints.this.repaint();
  162. //
  163. if (partsIdx == totalLen) {
  164. int choice = JOptionPane.showConfirmDialog(
  165. PoseAnno_BB_And_Joints.this,
  166. "complete! save annotation or not?",
  167. "tips",
  168. JOptionPane.YES_NO_OPTION
  169. );
  170. if (choice == JOptionPane.YES_OPTION) {
  171. // save the annotation
  172. saveAnnotation();
  173. // set next image
  174. if (file_list.size() > 1) {
  175. setNextPic(1);
  176. }
  177. }
  178. }
  179. }
  180. });
  181. addMouseMotionListener(new MouseMotionAdapter() {
  182. @Override
  183. public void mouseMoved(MouseEvent e) {
  184. /*if (initImage == null || partsIdx == totalLen) {
  185. return;
  186. }*/
  187. // only for drawing a bounding box for the whole person
  188. // not for the 15 body joints
  189. if (initImage == null || partsIdx > 1) {
  190. return;
  191. }
  192. // System.out.println("mouse move");
  193. // get the point location -- keep lineIdx
  194. xPoints[partsIdx] = e.getX() - _startJframeX;
  195. yPoints[partsIdx] = e.getY() - _startJframeY;
  196. // System.out.println("mouse move point: (" + e.getX() +
  197. // ", " + e.getY() + ")");
  198. System.out.println("mouse click point: (" + xPoints[partsIdx]
  199. + ", " + yPoints[partsIdx] + ")");
  200. if (xPoints[partsIdx] < _border) {
  201. xPoints[partsIdx] = _border;
  202. }
  203. if (xPoints[partsIdx] >= initImage.getWidth(null) + _border) {
  204. xPoints[partsIdx] = initImage.getWidth(null) + _border - 1;
  205. }
  206. if (yPoints[partsIdx] < _border) {
  207. yPoints[partsIdx] = _border;
  208. }
  209. if (yPoints[partsIdx] >= initImage.getHeight(null) + _border) {
  210. yPoints[partsIdx] = initImage.getHeight(null) + _border - 1;
  211. }
  212. // repaint
  213. repaint();
  214. }
  215. });
  216. // o: 0: non-occluded, 1: occluded
  217. // u: back to pre joint
  218. // n: ahead to next-joint
  219. // r: reset the current image
  220. // s: save the current anno info
  221. // a: back to pre image
  222. // d: ahead to next image
  223. addKeyListener(new KeyListener() {
  224. @Override
  225. public void keyTyped(KeyEvent e) {
  226. }
  227. // dose not modify
  228. @Override
  229. public void keyReleased(KeyEvent e) {
  230. switch (e.getKeyCode()) {
  231. case KeyEvent.VK_SHIFT:
  232. shift_flag = false;
  233. break;
  234. }
  235. }
  236. // need to be modified
  237. @Override
  238. public void keyPressed(KeyEvent e) {
  239. switch (Character.toLowerCase(e.getKeyChar())) {
  240. // back to pre-part
  241. case 'u':
  242. if (partsIdx != 0) {
  243. System.out.println("u(back to the pre joint):" + " " + annoName[partsIdx]);
  244. // current part -- need to be reset
  245. xPoints[partsIdx] = 0;
  246. yPoints[partsIdx] = 0;
  247. isOccluded[partsIdx] = 0; // set to be non-occlusion
  248. partsIdx--;
  249. // need to be reset
  250. if (partsIdx < 0) {
  251. Arrays.fill(isOccluded, 0);
  252. Arrays.fill(xPoints, 0);
  253. Arrays.fill(yPoints, 0);
  254. partsIdx = 0;
  255. return;
  256. }
  257. // current part -- need to be reset
  258. // really need?
  259. xPoints[partsIdx] = 0;
  260. yPoints[partsIdx] = 0;
  261. isOccluded[partsIdx] = 0; // set to be non-occlusion
  262. }
  263. break;
  264. // save the annotation
  265. case 's':
  266. System.out.println("s(save)" + " " + file_list.get(curFileIdx));
  267. saveAnnotation();
  268. // set and init next image
  269. if (file_list.size() > 1) {
  270. setNextPic(1);
  271. }
  272. break;
  273. // ahead to next part
  274. case 'n':
  275. // skip current annotation due to missing
  276. if (partsIdx >= annoName.length)
  277. return;
  278. System.out.println("n(next joint): " + " " + annoName[partsIdx]);
  279. xPoints[partsIdx] = 0;
  280. yPoints[partsIdx] = 0;
  281. // set to be occlusion since you ignore the current part
  282. isOccluded[partsIdx] = 1;
  283. partsIdx++;
  284. if (partsIdx == totalLen) {
  285. int choice = JOptionPane.showConfirmDialog(
  286. PoseAnno_BB_And_Joints.this,
  287. "complete! save annotation or not?", "tips",
  288. JOptionPane.YES_NO_OPTION
  289. );
  290. if (choice == JOptionPane.YES_OPTION) {
  291. //
  292. saveAnnotation();
  293. // set and init next image
  294. // if (file_list.size() > 1) {
  295. // setNextPic(1);
  296. // }
  297. }
  298. }
  299. break;
  300. // back to pre image
  301. case 'a':
  302. // back to pre image and init pre image status
  303. System.out.println("a(back from):" + " " + file_list.get(curFileIdx));
  304. // remove the current label file, if it exist
  305. String annoPath = _prePath + "/" + _imagesetName + _saveDirExt;
  306. File file = new File(annoPath);
  307. file.mkdir();
  308. String annoLabelFile = annoPath + "/" + fileName + _labelExt;
  309. file = new File(annoLabelFile);
  310. if(file.exists()) {
  311. file.delete();
  312. }
  313. // then back to pre image
  314. setNextPic(0);
  315. break;
  316. // ahead to next image
  317. case 'd':
  318. // set and init next image status
  319. // saveAnnotation();
  320. // just ignore the current image
  321. System.out.println("d(ahead to):" + " " + file_list.get(curFileIdx));
  322. setNextPic(1);
  323. break;
  324. // reset the current image
  325. case 'r':
  326. System.out.println("r(rest):" + " " + file_list.get(curFileIdx));
  327. partsIdx = 0;
  328. Arrays.fill(xPoints, 0);
  329. Arrays.fill(yPoints, 0);
  330. Arrays.fill(isOccluded, 0);
  331. break;
  332. // 1: partial occluded
  333. case 'o':
  334. System.out.println("if the part is occluded, you should " +
  335. "press the 'o' key before click the mouse");
  336. System.out.println("o(occ): " + " " + annoName[partsIdx]);
  337. isOccluded[partsIdx] = 1;
  338. break;
  339. default:
  340. switch (e.getKeyCode()) {
  341. case KeyEvent.VK_ESCAPE:
  342. xPoints[partsIdx] = 0;
  343. yPoints[partsIdx] = 0;
  344. isOccluded[partsIdx] = 1; // set to be non-occlusion
  345. break;
  346. case KeyEvent.VK_SHIFT:
  347. shift_flag = true;
  348. break;
  349. }
  350. }
  351. // repaint
  352. PoseAnno_BB_And_Joints.this.repaint();
  353. }
  354. });
  355. new DropTarget(this, this);
  356. setVisible(true);
  357. }
  358. protected void saveAnnotation() {
  359. saveAnnoPose();
  360. }
  361. protected void saveAnnoPose() {
  362. try {
  363. String annoPath = _prePath + "/" + _imagesetName + _saveDirExt;
  364. File file = new File(annoPath);
  365. file.mkdir();
  366. String annoLabelFile = annoPath + "/" + fileName + _labelExt;
  367. file = new File(annoLabelFile);
  368. BufferedWriter bw = new BufferedWriter(new FileWriter(file));
  369. // the total path to the image
  370. String str = fileName2 + "\n";
  371. double x1, y1;
  372. // body joints
  373. for (int i = 0; i < totalLen - 1; i++) {
  374. x1 = Math.max( (xPoints[i] - _border) / scale, 0 );
  375. y1 = Math.max( (yPoints[i] - _border) / scale, 0 );
  376. str = str + annoName[i] + " " + x1 + " " + y1 + " "
  377. + isOccluded[i] + "\n";
  378. }
  379. x1 = Math.max( (xPoints[totalLen - 1] - _border) / scale, 0 );
  380. y1 = Math.max( (yPoints[totalLen - 1] - _border) / scale, 0 );
  381. str = str + annoName[totalLen - 1] + " " + x1 + " " + y1 + " "
  382. + isOccluded[totalLen - 1];
  383. str = str + "\n";
  384. bw.write(str);
  385. bw.close();
  386. } catch (Exception e) {
  387. e.printStackTrace();
  388. JOptionPane.showMessageDialog(this, "failed to save annotation");
  389. }
  390. }
  391. @Override
  392. public void update(Graphics g) {
  393. if (initImage == null)
  394. return;
  395. if (offScreenImage == null
  396. || offScreenImage.getHeight(this) != this.getHeight()
  397. || offScreenImage.getWidth(this) != this.getWidth() ) {
  398. offScreenImage = this.createImage(this.getWidth(), this.getHeight());
  399. gOffScreenGraphics = offScreenImage.getGraphics();
  400. }
  401. int imgWidth = initImage.getWidth(null);
  402. int imgHeigth = initImage.getHeight(null);
  403. int osImgWidth = offScreenImage.getWidth(null);
  404. int osImgHeight = offScreenImage.getHeight(null);
  405. System.out.println("imgWidth: " + imgWidth + ", imgHeight: " + imgHeigth +
  406. ", oImgWidth: " + osImgWidth + ", oImgHeight: " + osImgHeight);
  407. // a top-left corner (_border, _border)
  408. gOffScreenGraphics.drawImage(initImage, _border, _border, this);
  409. ((Graphics2D) gOffScreenGraphics).setStroke(new BasicStroke(1.50f));
  410. gOffScreenGraphics.setColor(Color.GREEN);
  411. if (partsIdx < totalLen) {
  412. gOffScreenGraphics.drawString("" + partsIdx + ": " + annoName[partsIdx], 20, 20);
  413. } else {
  414. gOffScreenGraphics.drawString("" + partsIdx + ": " +
  415. "annotatation has been done!", 20, 50);
  416. }
  417. gOffScreenGraphics.setColor(Color.ORANGE);
  418. // draw the current line
  419. int i;
  420. // draw the joints
  421. for(i = 0; i < partsIdx; i++) {
  422. gOffScreenGraphics.fillOval(xPoints[i], yPoints[i], _circle_len, _circle_len);
  423. }
  424. // draw the bounding box for human
  425. gOffScreenGraphics.setColor(Color.RED);
  426. if(partsIdx > 1) {
  427. int lineX[] = {xPoints[0], xPoints[0], xPoints[1], xPoints[1], xPoints[0]};
  428. int lineY[] = {yPoints[0], yPoints[1], yPoints[1], yPoints[0], yPoints[0]};
  429. for (i = 0; i < lineX.length - 1; i++) {
  430. gOffScreenGraphics.drawLine(lineX[i], lineY[i], lineX[i + 1], lineY[i + 1]);
  431. }
  432. }
  433. // paint(gOffScreen);
  434. g.drawImage(offScreenImage, _startJframeX, _startJframeY, this);
  435. g.dispose();
  436. }
  437. @Override
  438. public void paint(Graphics g) {
  439. update(g);
  440. }
  441. // *********************************************
  442. @Override
  443. public void dragEnter(DropTargetDragEvent dtde) {
  444. }
  445. @Override
  446. public void dragOver(DropTargetDragEvent dtde) {
  447. }
  448. @Override
  449. public void dropActionChanged(DropTargetDragEvent dtde) {
  450. }
  451. @Override
  452. public void dragExit(DropTargetEvent dte) {
  453. }
  454. // *******************************************
  455. // drop the images into UI
  456. // get the image file list
  457. @SuppressWarnings("rawtypes")
  458. @Override
  459. public void drop(DropTargetDropEvent dtde) {
  460. try {
  461. if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
  462. dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
  463. List list = (List) (dtde.getTransferable()
  464. .getTransferData(DataFlavor.javaFileListFlavor));
  465. Iterator iterator = list.iterator();
  466. file_list.clear();
  467. curFileIdx = -1;
  468. while (iterator.hasNext()) {
  469. File file = (File) iterator.next();
  470. if (file.isDirectory()) {
  471. addFileToQueue(file);
  472. } else {
  473. if (file.getName().endsWith(".jpg") ||
  474. file.getName().endsWith(".png"))
  475. file_list.add(file);
  476. }
  477. }
  478. // start & init
  479. setNextPic(1);
  480. dtde.dropComplete(true);
  481. } else {
  482. dtde.rejectDrop();
  483. }
  484. } catch (Exception e) {
  485. e.printStackTrace();
  486. dtde.rejectDrop();
  487. JOptionPane.showMessageDialog(this, "Failed to open images");
  488. }
  489. }
  490. // flag == 0 -- previous pic
  491. // flag == 1 -- next pic
  492. private void setNextPic(int flag) {
  493. int tmp = curFileIdx;
  494. // back
  495. if (flag == 0) {
  496. if (curFileIdx == 0) {
  497. JOptionPane.showMessageDialog(this, "The first one!");
  498. return;
  499. }
  500. curFileIdx--;
  501. } else {
  502. // next
  503. curFileIdx++;
  504. }
  505. boolean ok = false;
  506. while (!ok) {
  507. try {
  508. if (curFileIdx >= file_list.size()) {
  509. curFileIdx = tmp;
  510. if (curFileIdx == -1)
  511. return;
  512. JOptionPane.showMessageDialog(this, "The last one!");
  513. return;
  514. }
  515. File file = file_list.get(curFileIdx);
  516. initImage = ImageIO.read(file);
  517. initImage = initImage.getScaledInstance(
  518. (int) (initImage.getWidth(null) * scale),
  519. (int) (initImage.getHeight(null) * scale),
  520. Image.SCALE_SMOOTH // SCALE_DEFAULT, SCALE_SMOOTH
  521. );
  522. // path and fileName
  523. int idx;
  524. path = file.getParent();
  525. String fileSeparator = System.getProperty("file.separator");
  526. // idx = path.lastIndexOf('\\'); // here is '\'
  527. idx = path.lastIndexOf(fileSeparator); // here is '\'
  528. System.out.println("idx " + idx);
  529. System.out.println("fileSeparator " + fileSeparator);
  530. System.out.println("path " + path);
  531. if(idx != 0){
  532. _prePath = path.substring(0, idx);
  533. _imagesetName = path.substring(idx + 1);
  534. }
  535. int imgLen = file_list.size();
  536. fileName = file.getName();
  537. fileName2 = path + fileSeparator + file.getName();
  538. idx = fileName.lastIndexOf('.');
  539. if (idx != -1) {
  540. fileName = fileName.substring(0, idx);
  541. }
  542. setTitle(fileName + " in " + imgLen + " imageset");
  543. //
  544. partsIdx = 0;
  545. // set default value - 0: non_occ, 1: occ
  546. Arrays.fill(isOccluded, 0);
  547. // 1 bb for person and 15 joints
  548. Arrays.fill(points, 0);
  549. Arrays.fill(xPoints, 0);
  550. Arrays.fill(yPoints, 0);
  551. // _border: 10
  552. // _startJframeX: 0
  553. // _startJframeY: 20
  554. setSize(initImage.getWidth(null) + _border * 3 + _startJframeX,
  555. initImage.getHeight(null) + _border * 3 + _startJframeY
  556. );
  557. // readAnnotation(path + "/Annotations/" + fileName + ".xml");
  558. repaint();
  559. ok = true;
  560. } catch (Exception e) {
  561. e.printStackTrace();
  562. if (flag == 0) {
  563. if (curFileIdx == 0) {
  564. return;
  565. }
  566. curFileIdx--;
  567. } else {
  568. curFileIdx++;
  569. }
  570. }
  571. }
  572. }
  573. // add file to file_list
  574. // if meet folder then recur it
  575. private void addFileToQueue(File folder) {
  576. for (File file : folder.listFiles()) {
  577. if (file.isDirectory()) {
  578. addFileToQueue(file);
  579. } else {
  580. if (file.getName().endsWith(".jpg") || file.getName().endsWith(".png")) {
  581. file_list.add(file);
  582. }
  583. }
  584. }
  585. }
  586. // here we don't use it
  587. private boolean readAnnotation(String path) {
  588. File gt = new File(path);
  589. if (!gt.exists())
  590. return false;
  591. int choice = JOptionPane.showConfirmDialog(this,
  592. "Annotation file is found, read it or not?", "tips",
  593. JOptionPane.YES_NO_OPTION);
  594. if (choice != JOptionPane.YES_OPTION)
  595. return false;
  596. try {
  597. // ??
  598. } catch (Exception e) {
  599. e.printStackTrace();
  600. partsIdx = 0;
  601. Arrays.fill(xPoints, 0);
  602. Arrays.fill(yPoints, 0);
  603. Arrays.fill(isOccluded, 0);
  604. JOptionPane.showMessageDialog(this, "Invalid annotation file");
  605. }
  606. return true;
  607. }
  608. // **********************************************
  609. public static void main(String[] args) {
  610. new PoseAnno_BB_And_Joints();
  611. }
  612. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注