00001
00033 package april.ros;
00034
00035 import org.apache.commons.logging.Log;
00036 import org.ros.message.MessageListener;
00037 import org.ros.message.MessageFactory;
00038 import org.ros.message.Duration;
00039 import org.ros.namespace.GraphName;
00040 import org.ros.node.AbstractNodeMain;
00041 import org.ros.node.ConnectedNode;
00042 import org.ros.node.NodeMain;
00043 import org.ros.node.topic.Subscriber;
00044 import org.ros.node.topic.Publisher;
00045 import org.ros.node.parameter.ParameterTree;
00046 import org.ros.rosjava_geometry.*;
00047
00048 import april.tag.*;
00049 import april.util.*;
00050 import april.jmat.*;
00051
00052 import java.awt.image.*;
00053 import java.awt.Color;
00054 import java.awt.color.*;
00055 import java.awt.*;
00056 import java.util.ArrayList;
00057 import java.io.File;
00058 import java.io.InputStream;
00059 import java.io.IOException;
00060 import java.io.ByteArrayInputStream;
00061 import javax.imageio.ImageIO;
00062
00063 import java.lang.System;
00064 import java.lang.Math;
00065
00066 public class TagPublisher extends AbstractNodeMain {
00067
00068 private boolean focalLengthAvailable = false;
00069
00070 private TagDetector detector;
00071
00072 private double focalLengthX;
00073 private double focalLengthY;
00074
00078 public static BufferedImage messageToBufferedImage(
00079 sensor_msgs.Image message) {
00080
00081 BufferedImage im =
00082 new BufferedImage(message.getWidth(),
00083 message.getHeight(),
00084 BufferedImage.TYPE_INT_RGB);
00085
00086 for (int x = 0; x < message.getWidth(); x++) {
00087 for (int y = 0; y < message.getHeight(); y++) {
00088 byte red =
00089 message.getData().getByte((int) (y * message.getStep() + 3 * x));
00090 byte green =
00091 message.getData().getByte((int) (y * message.getStep() + 3 * x + 1));
00092 byte blue =
00093 message.getData().getByte((int) (y * message.getStep() + 3 * x + 2));
00094 int rgb = (red & 0xFF);
00095 rgb = (rgb << 8) + (green & 0xFF);
00096 rgb = (rgb << 8) + (blue & 0xFF);
00097 im.setRGB(x, y, rgb);
00098 }
00099 }
00100
00101 return im;
00102 }
00103
00114 public static BufferedImage messageToBufferedImageFast(
00115 sensor_msgs.Image message) {
00116
00117 int width = message.getWidth();
00118 int height = message.getHeight();
00119
00120 DataBufferByte dataBuffer =
00121 new DataBufferByte(message.getData().array(),
00122 message.getData().array().length);
00123 PixelInterleavedSampleModel sampleModel =
00124 new PixelInterleavedSampleModel(
00125 DataBuffer.TYPE_BYTE, width, height, 3, 3*width, new int[] {2,1,0});
00126 WritableRaster raster =
00127 Raster.createWritableRaster(sampleModel, dataBuffer, new Point(0,0));
00128
00129 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
00130 ColorModel colourModel = new ComponentColorModel(
00131 cs, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
00132
00133 BufferedImage im = new BufferedImage(colourModel, raster, false, null);
00134
00135 return im;
00136 }
00137
00144 public static Vector3 projectionMatrixToTranslationVector(double M[][]) {
00145 Vector3 point = new Vector3(M[0][3], -M[1][3], -M[2][3]);
00146 return point;
00147 }
00148
00153 public static Quaternion projectionMatrixToQuaternion(double M[][]) {
00154
00155 double qx, qy, qz, qw;
00156
00157 double t = M[0][0] + M[1][1] + M[2][2];
00158 if (t > 0) {
00159 double r = Math.sqrt(1+t);
00160 double s = 0.5f / r;
00161 qw = 0.5 * r;
00162 qx = (M[2][1] - M[1][2]) * s;
00163 qy = (M[0][2] - M[2][0]) * s;
00164 qz = (M[1][0] - M[0][1]) * s;
00165 } else {
00166 if (M[0][0] > M[1][1] && M[0][0] > M[2][2]) {
00167 double r = Math.sqrt(1.0f + M[0][0] - M[1][1] - M[2][2]);
00168 double s = 0.5f / r;
00169 qw = (M[2][1] - M[1][2]) * s;
00170 qx = 0.5f * r;
00171 qy = (M[0][1] + M[1][0]) * s;
00172 qz = (M[2][0] + M[0][2]) * s;
00173 } else if (M[1][1] > M[2][2]) {
00174 double r = Math.sqrt(1.0f + M[1][1] - M[0][0] - M[2][2]);
00175 double s = 0.5f / r;
00176 qw = (M[0][2] - M[2][0]) * s;
00177 qx = (M[0][1] + M[1][0]) * s;
00178 qy = 0.5f * r;
00179 qz = (M[1][2] + M[2][1]) * s;
00180 } else {
00181 double r = Math.sqrt(1.0f + M[2][2] - M[0][0] - M[1][1]);
00182 double s = 0.5f / r;
00183 qw = (M[1][0] - M[0][1]) * s;
00184 qx = (M[0][2] + M[2][0]) * s;
00185 qy = (M[1][2] + M[2][1]) * s;
00186 qz = 0.5f * r;
00187 }
00188 }
00189
00190 return new Quaternion(qx, -qy, -qz, qw);
00191 }
00192
00195 public static Transform projectionMatrixToTransform(double M[][]) {
00196 return new Transform(TagPublisher.projectionMatrixToTranslationVector(M),
00197 TagPublisher.projectionMatrixToQuaternion(M));
00198 }
00199
00200 public static void detectionToTagPose(TagDetection d, Transform transform,
00201 april_msgs.TagPose tag, MessageFactory msgFactory) {
00202
00203
00204 tag.setId(d.id);
00205 tag.setHammingDistance(d.hammingDistance);
00206
00207
00208 double p0[] = d.interpolate(-1,-1);
00209 double p1[] = d.interpolate(1,-1);
00210 double p2[] = d.interpolate(1,1);
00211 double p3[] = d.interpolate(-1,1);
00212 java.util.List<geometry_msgs.Point32> imageCoordinates =
00213 tag.getImageCoordinates();
00214
00215 geometry_msgs.Point32 pt0 = msgFactory.
00216 newFromType(geometry_msgs.Point32._TYPE);
00217 pt0.setX((float)p0[0]);
00218 pt0.setY((float)p0[1]);
00219 pt0.setZ(0);
00220 imageCoordinates.add(pt0);
00221
00222 geometry_msgs.Point32 pt1 = msgFactory.
00223 newFromType(geometry_msgs.Point32._TYPE);
00224 pt1.setX((float)p1[0]);
00225 pt1.setY((float)p1[1]);
00226 pt1.setZ(0);
00227 imageCoordinates.add(pt1);
00228
00229 geometry_msgs.Point32 pt2 = msgFactory.
00230 newFromType(geometry_msgs.Point32._TYPE);
00231 pt2.setX((float)p2[0]);
00232 pt2.setY((float)p2[1]);
00233 pt2.setZ(0);
00234 imageCoordinates.add(pt2);
00235
00236 geometry_msgs.Point32 pt3 = msgFactory.
00237 newFromType(geometry_msgs.Point32._TYPE);
00238 pt3.setX((float)p3[0]);
00239 pt3.setY((float)p3[1]);
00240 pt3.setZ(0);
00241 imageCoordinates.add(pt3);
00242
00243
00244 transform.toPoseMessage(tag.getPose());
00245 }
00246
00247 public static void tagPoseToVisualizationMarker(april_msgs.TagPose tag,
00248 visualization_msgs.Marker marker, double tagVisMagnification,
00249 double tagSize, MessageFactory msgFactory) {
00250 marker.setNs("tag" + tag.getId());
00251 marker.setId(0);
00252 marker.setAction(visualization_msgs.Marker.ADD);
00253 marker.setPose(tag.getPose());
00254 marker.setLifetime(new Duration(1.0));
00255
00256
00257 marker.setType(visualization_msgs.Marker.LINE_STRIP);
00258 marker.getScale().setX(0.02);
00259 java.util.List<geometry_msgs.Point> points = marker.getPoints();
00260
00261 geometry_msgs.Point mpt0 = msgFactory.
00262 newFromType(geometry_msgs.Point._TYPE);
00263 mpt0.setX(tagVisMagnification * tagSize/2);
00264 mpt0.setY(tagVisMagnification * tagSize/2);
00265 mpt0.setZ(0);
00266 points.add(mpt0);
00267
00268 geometry_msgs.Point mpt1 = msgFactory.
00269 newFromType(geometry_msgs.Point._TYPE);
00270 mpt1.setX(tagVisMagnification * tagSize/2);
00271 mpt1.setY(-tagVisMagnification * tagSize/2);
00272 mpt1.setZ(0);
00273 points.add(mpt1);
00274
00275 geometry_msgs.Point mpt2 = msgFactory.
00276 newFromType(geometry_msgs.Point._TYPE);
00277 mpt2.setX(-tagVisMagnification * tagSize/2);
00278 mpt2.setY(-tagVisMagnification * tagSize/2);
00279 mpt2.setZ(0);
00280 points.add(mpt2);
00281
00282 geometry_msgs.Point mpt3 = msgFactory.
00283 newFromType(geometry_msgs.Point._TYPE);
00284 mpt3.setX(-tagVisMagnification * tagSize/2);
00285 mpt3.setY(+tagVisMagnification * tagSize/2);
00286 mpt3.setZ(0);
00287 points.add(mpt3);
00288
00289 points.add(mpt0);
00290 marker.getColor().setR(0.6f);
00291 marker.getColor().setG(0.6f);
00292 marker.getColor().setB(0.6f);
00293 marker.getColor().setA(1);
00294
00295
00296
00297
00298
00299
00300
00301 }
00302
00303 public static void tagPoseToVisualizationText(april_msgs.TagPose tag,
00304 visualization_msgs.Marker marker) {
00305 marker.setNs("tag" + tag.getId());
00306 marker.setId(1);
00307 marker.setType(visualization_msgs.Marker.TEXT_VIEW_FACING);
00308 marker.setAction(visualization_msgs.Marker.ADD);
00309 marker.setPose(tag.getPose());
00310 marker.getScale().setZ(0.2);
00311 marker.setText("" + tag.getId());
00312 marker.getColor().setR(0.6f);
00313 marker.getColor().setG(0.6f);
00314 marker.getColor().setB(0.6f);
00315 marker.getColor().setA(1);
00316 marker.setLifetime(new Duration(1.0));
00317 }
00318
00319 @Override
00320 public GraphName getDefaultNodeName() {
00321 return GraphName.of("april_tag_publisher");
00322 }
00323
00324 @Override
00325 public void onStart(final ConnectedNode node) {
00326
00327
00328 final Log log = node.getLog();
00329
00330
00331 ParameterTree params = node.getParameterTree();
00332
00333
00334 final String tagFamilyStr =
00335 params.getString("~tag_family", "april.tag.Tag36h11");
00336 final TagFamily tagFamily =
00337 (TagFamily) ReflectUtil.createObject(tagFamilyStr);
00338 detector = new TagDetector(tagFamily);
00339
00340
00341 double segSigma = params.getDouble("~seg_sigma", detector.segSigma);
00342 double sigma = params.getDouble("~sigma", detector.sigma);
00343 double minMag = params.getDouble("~min_mag", detector.minMag);
00344 double maxEdgeCost =
00345 params.getDouble("~max_edge_cost", detector.maxEdgeCost);
00346 double magThresh = params.getDouble("~mag_thresh", detector.magThresh);
00347 double thetaThresh =
00348 params.getDouble("~theta_thresh", detector.thetaThresh);
00349 int errorBits = params.getInteger("~error_bits", 1);
00350 int weightScale =
00351 params.getInteger("~weight_scale", detector.WEIGHT_SCALE);
00352 boolean segDecimate =
00353 params.getBoolean("~seg_decimate", detector.segDecimate);
00354 boolean debug = params.getBoolean("~debug", false);
00355
00356 detector.debug = debug;
00357 detector.sigma = sigma;
00358 detector.segSigma = segSigma;
00359 detector.segDecimate = segDecimate;
00360 detector.minMag = minMag;
00361 detector.maxEdgeCost = maxEdgeCost;
00362 detector.magThresh = magThresh;
00363 detector.thetaThresh = thetaThresh;
00364 detector.WEIGHT_SCALE = weightScale;
00365
00366
00367 final double tagSize = params.getDouble("~tag_size", 0.095);
00368 boolean useCameraInfo = params.getBoolean("~use_camera_info", true);
00369 this.focalLengthX = params.getDouble("~focal_length_x", 485.6);
00370 this.focalLengthY = params.getDouble("~focal_length_y", this.focalLengthX);
00371 if (!useCameraInfo) {
00372 focalLengthAvailable = true;
00373 log.info("Focal length values fx = " + focalLengthX +
00374 ", fy = " + focalLengthY);
00375 }
00376
00377
00378 final boolean publishVisualization =
00379 params.getBoolean("~publish_visualization", true);
00380 final double tagVisMagnification =
00381 params.getDouble("~tag_vis_magnification", 1);
00382 final boolean broadcastTf =
00383 params.getBoolean("~broadcast_tf", true);
00384
00385
00386 final Publisher<april_msgs.TagPoseArray> publisher =
00387 node.newPublisher("tags", april_msgs.TagPoseArray._TYPE);
00388
00389
00390 final Publisher<visualization_msgs.MarkerArray> visPublisher;
00391 visPublisher = node.newPublisher("/visualization_marker_array",
00392 visualization_msgs.MarkerArray._TYPE);
00393
00394
00395 final Publisher<tf.tfMessage> tfPublisher;
00396 tfPublisher = node.newPublisher("/tf", tf.tfMessage._TYPE);
00397
00398
00399 Subscriber<sensor_msgs.Image> image_subscriber =
00400 node.newSubscriber("image_raw", sensor_msgs.Image._TYPE);
00401 image_subscriber.addMessageListener(
00402 new MessageListener<sensor_msgs.Image>() {
00403 @Override
00404 public void onNewMessage(sensor_msgs.Image message) {
00405
00406
00407 if (!focalLengthAvailable) {
00408 log.debug("Waiting for camera info message");
00409 return;
00410 }
00411
00412
00413 BufferedImage im = TagPublisher.messageToBufferedImage(message);
00414 double[] size = new double[] {im.getWidth()/2.0, im.getHeight()/2.0};
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425 ArrayList<TagDetection> detections = detector.process(im, size);
00426
00427
00428 april_msgs.TagPoseArray tagArray = publisher.newMessage();
00429 tagArray.setHeader(message.getHeader());
00430 java.util.List<april_msgs.TagPose> tags = tagArray.getTags();
00431
00432 visualization_msgs.MarkerArray visArray;
00433 java.util.List<visualization_msgs.Marker> markers;
00434 visArray = visPublisher.newMessage();
00435 markers = visArray.getMarkers();
00436
00437 tf.tfMessage tfMsg = tfPublisher.newMessage();
00438 java.util.List<geometry_msgs.TransformStamped> transforms =
00439 tfMsg.getTransforms();
00440
00441
00442 for (TagDetection d : detections) {
00443 double M[][] = CameraUtil.homographyToPose(
00444 focalLengthX, focalLengthY, tagSize, d.homography);
00445
00446
00447 Transform transform = TagPublisher.projectionMatrixToTransform(M);
00448
00449
00450 april_msgs.TagPose tag = node.getTopicMessageFactory().
00451 newFromType(april_msgs.TagPose._TYPE);
00452 TagPublisher.detectionToTagPose(d, transform, tag,
00453 node.getTopicMessageFactory());
00454 tags.add(tag);
00455
00456
00457 if (publishVisualization) {
00458 visualization_msgs.Marker marker = node.getTopicMessageFactory().
00459 newFromType(visualization_msgs.Marker._TYPE);
00460 marker.setHeader(message.getHeader());
00461 TagPublisher.tagPoseToVisualizationMarker(tag, marker,
00462 tagVisMagnification, tagSize, node.getTopicMessageFactory());
00463 markers.add(marker);
00464
00465 visualization_msgs.Marker markerText =
00466 node.getTopicMessageFactory().
00467 newFromType(visualization_msgs.Marker._TYPE);
00468 markerText.setHeader(message.getHeader());
00469 TagPublisher.tagPoseToVisualizationText(tag, markerText);
00470 markers.add(markerText);
00471 }
00472
00473
00474 if (broadcastTf) {
00475 geometry_msgs.TransformStamped transformMsg =
00476 node.getTopicMessageFactory().
00477 newFromType(geometry_msgs.TransformStamped._TYPE);
00478 transformMsg.setHeader(message.getHeader());
00479 transformMsg.setChildFrameId(tagFamilyStr + "." + tag.getId());
00480 transform.toTransformMessage(transformMsg.getTransform());
00481 transforms.add(transformMsg);
00482 }
00483 }
00484
00485
00486 publisher.publish(tagArray);
00487 if (publishVisualization) {
00488 visPublisher.publish(visArray);
00489 }
00490 if (broadcastTf) {
00491 tfPublisher.publish(tfMsg);
00492 }
00493 }
00494 });
00495
00496
00497 if (useCameraInfo) {
00498 Subscriber<sensor_msgs.CameraInfo> info_subscriber =
00499 node.newSubscriber("camera_info", sensor_msgs.CameraInfo._TYPE);
00500 info_subscriber.addMessageListener(
00501 new MessageListener<sensor_msgs.CameraInfo>() {
00502 @Override
00503 public void onNewMessage(sensor_msgs.CameraInfo message) {
00504 if (!focalLengthAvailable) {
00505 log.info("Camera Info message received");
00506 double[] k = message.getK();
00507 if (k.length != 9) {
00508 log.error("Camera info message has malformed or non-existent " +
00509 "intrinsic camera matrix; unable to retrieve focal length. " +
00510 "Using default values!");
00511 } else {
00512 focalLengthX = k[0];
00513 focalLengthY = k[4];
00514 log.info("Focal length selected from camera info message");
00515 }
00516 log.info("Focal length values fx = " + focalLengthX +
00517 ", fy = " + focalLengthY);
00518 focalLengthAvailable = true;
00519 }
00520 }
00521 });
00522 }
00523
00524 }
00525 }