/* * 作成日: 2008/06/18 */ package jp.ac.fit.asura.nao.vision.perception; import java.awt.Polygon; import java.awt.Rectangle; import javax.vecmath.Point2f; import jp.ac.fit.asura.nao.misc.Coordinates; import jp.ac.fit.asura.nao.misc.MathUtils; import jp.ac.fit.asura.nao.sensation.SomaticContext; import jp.ac.fit.asura.nao.vision.perception.BlobVision.Blob; import org.apache.log4j.Logger; /** * @author sey * * @version $Id: GeneralVision.java 704 2008-10-23 17:25:51Z sey $ * */ public class GeneralVision extends AbstractVision { private static final Logger log = Logger.getLogger(GeneralVision.class); public void processObject(VisualObject obj) { calcCenter(obj); calcAngle(obj); calcArea(obj); checkTouched(obj); calcConfidence(obj); } private void checkTouched(VisualObject obj) { obj.isTopTouched = false; obj.isBottomTouched = false; obj.isLeftTouched = false; obj.isRightTouched = false; for (Blob blob : obj.getBlobs()) { if (blob.xmin == 0) obj.isLeftTouched = true; if (blob.xmax == getContext().image.getWidth() - 1) obj.isRightTouched = true; if (blob.ymin == 0) obj.isTopTouched = true; if (blob.ymax == getContext().image.getHeight() - 1 || blob.ymax == getContext().image.getHeight() - 2) obj.isBottomTouched = true; } } /** * 引数で与えられたオブジェクトの中心位置のイメージ座標系での角度を求めます. * * @param obj */ private void calcAngle(VisualObject obj) { calculateImageAngle(obj.center, obj.angle); calculateRobotAngle(obj.angle, obj.robotAngle); log.trace(obj.angle + " : " + obj.robotAngle); } /** * 引数で与えられたオブジェクトの中心位置を求めます. * * この実装は単純にblobの中心を求めています. * * @param obj */ private void calcCenter(VisualObject obj) { Blob blob = obj.getBlobs().iterator().next(); Point2f cp = obj.center; cp.x = (blob.xmin + blob.xmax) / 2; cp.y = (blob.ymin + blob.ymax) / 2; } private void calcConfidence(VisualObject obj) { int cf = 0; if (!obj.getBlobs().isEmpty()) { // すごくやる気のないWebots対応. // schemeから定数を設定できるようにすべき. if (getVisualFrame().getImage().getWidth() == 320) cf = obj.getBlobs().iterator().next().mass * 4; else cf = obj.getBlobs().iterator().next().mass * 64; cf = MathUtils.clipping(cf, 0, 1000); if (obj.isLeftTouched()) cf /= 2; if (obj.isRightTouched()) cf /= 2; if (obj.isTopTouched()) cf /= 2; if (obj.isBottomTouched()) cf /= 2; } obj.confidence = cf; } private void calcArea(VisualObject obj) { Rectangle rect = obj.area; Blob first = obj.getBlobs().iterator().next(); rect.x = first.xmin; rect.y = first.ymin; rect.width = first.xmax - first.xmin + 1; rect.height = first.ymax - first.ymin + 1; for (Blob blob : obj.getBlobs()) { int xmax = Math.max(rect.x + rect.width - 1, blob.xmax); int ymax = Math.max(rect.y + rect.height - 1, blob.ymax); rect.x = Math.min(rect.x, blob.xmin); rect.y = Math.min(rect.y, blob.ymin); rect.width = xmax - rect.x + 1; rect.height = ymax - rect.y + 1; } } /** * VisualObjectを取り囲む多角形(Polygon)を計算する. * * 凸多角形(Convex hull)を計算するわけではないので注意. * * Objectの一部として判定するのは指定されたcolorのみ. * * @param obj * @param color */ protected void calcBoundary(VisualObject obj, byte color) { Polygon p = obj.polygon; byte[] plane = getContext().gcdPlane; int xmin = obj.area.x; int xmax = obj.area.x + obj.area.width - 1; int ymin = obj.area.y; int ymax = obj.area.y + obj.area.height - 1; int w = getContext().image.getWidth(); int begin = 0; // 上面 左, 右 int yidx = ymin * w; for (int x = xmin; x <= xmax; x++) if (plane[yidx + x] == color) { p.addPoint(x, ymin); begin = x; break; } for (int x = xmax; x >= xmin; x--) if (plane[yidx + x] == color) { if (begin != x) p.addPoint(x, ymin); break; } // 右面 上, 下 for (int i = ymin * w + xmax; i <= ymax * w + xmax; i += w) if (plane[i] == color) { p.addPoint(xmax, i / w); begin = i; break; } for (int i = ymax * w + xmax; i >= ymin * w + xmax; i -= w) if (plane[i] == color) { if (begin != i) p.addPoint(xmax, i / w); break; } // 下面 右, 左 yidx = ymax * w; for (int x = xmax; x >= xmin; x--) if (plane[yidx + x] == color) { p.addPoint(x, ymax); begin = x; break; } for (int x = xmin; x <= xmax; x++) if (plane[yidx + x] == color) { if (begin != x) p.addPoint(x, ymax); break; } // 左面 下, 上 for (int i = ymax * w + xmin; i >= ymin * w + xmin; i -= w) if (plane[i] == color) { p.addPoint(xmin, i / w); begin = i; break; } for (int i = ymin * w + xmin; i <= ymax * w + xmin; i += w) if (plane[i] == color) { if (begin != i) p.addPoint(xmin, i / w); break; } } /** * 与えられた点point(画像平面座標系)の、イメージ座標系(image)での角度を返します. * * 返される角度は、画像平面座標系(plane)ではないことに注意してください. * * @param planePoint * @param imageAngle */ private void calculateImageAngle(Point2f planePoint, Point2f imageAngle) { float hFov = getContext().camera.getHorizontalFieldOfView(); float vFov = getContext().camera.getVerticalFieldOfView(); // Coordinates.plane2imageCoord(getContext(), planePoint, imageAngle); imageAngle.x = imageAngle.x / getContext().image.getWidth() * hFov; imageAngle.y = imageAngle.y / getContext().image.getHeight() * vFov; } private void calculateRobotAngle(Point2f imageAngle, Point2f robotAngle) { SomaticContext sc = getMotionFrame().getSomaticContext(); Coordinates.image2cameraAngle(sc, imageAngle, robotAngle); Coordinates.camera2bodyAngle(sc, robotAngle, robotAngle); Coordinates.body2robotAngle(sc, robotAngle, robotAngle); } }