TagPublisher.java
Go to the documentation of this file.
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     // Tag id and hamming distance error
00204     tag.setId(d.id);
00205     tag.setHammingDistance(d.hammingDistance);
00206 
00207     // Image coordinates of Tag Detection
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     // p0
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     // p1
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     // p2
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     // p3
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     // Pose of the the tag
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     // Use Line strip
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     // p0
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     // p1
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     // p2
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     // p3
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     // p4
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     // Alternatively, use mesh (currently not working)
00296     /* marker.setType(visualization_msgs.Marker.MESH_RESOURCE); */
00297     /* marker.getScale().setX(tagSize * 5); */
00298     /* marker.getScale().setY(tagSize * 5); */
00299     /* marker.setMeshResource("package://april_tags_node/meshes/tag.stl"); */
00300     /* marker.setMeshUseEmbeddedMaterials(true); */
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     // Get logger to publish ROS log messages
00328     final Log log = node.getLog();
00329 
00330     // Get parameters from parameter server
00331     ParameterTree params = node.getParameterTree();
00332 
00333     // Create detector object
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     // Tag Detector Parameters
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     // Tag pose estimation parameters
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     // ROS specific parameters
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     // Create the publisher for the Tag Array
00386     final Publisher<april_msgs.TagPoseArray> publisher = 
00387       node.newPublisher("tags", april_msgs.TagPoseArray._TYPE);
00388 
00389     // Create the publisher for rviz visualization
00390     final Publisher<visualization_msgs.MarkerArray> visPublisher;
00391     visPublisher = node.newPublisher("/visualization_marker_array", 
00392           visualization_msgs.MarkerArray._TYPE);
00393 
00394     // Create publisher for broadcastin tf messages
00395     final Publisher<tf.tfMessage> tfPublisher;
00396     tfPublisher = node.newPublisher("/tf", tf.tfMessage._TYPE);
00397 
00398     // Subscribe to the image message    
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         // Do not process image messages if still waiting for camera info msg
00407         if (!focalLengthAvailable) {
00408           log.debug("Waiting for camera info message");
00409           return;
00410         }
00411 
00412         // Obtain Buffered Image from the ROS image message
00413         BufferedImage im = TagPublisher.messageToBufferedImage(message);
00414         double[] size = new double[] {im.getWidth()/2.0, im.getHeight()/2.0};
00415 
00416         // try {
00417         //     // retrieve image
00418         //     File outputfile = new File("/home/piyushk/saved.png");
00419         //     ImageIO.write(im, "png", outputfile);
00420         // } catch (IOException e) {
00421         //   log.error("unable to save image");
00422         // }
00423 
00424         // Detect tags!
00425         ArrayList<TagDetection> detections = detector.process(im, size);
00426 
00427         // Setup messages to publish detection information
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         // For each tag, add the pose to the tag list
00442         for (TagDetection d : detections) {
00443           double M[][] = CameraUtil.homographyToPose(
00444               focalLengthX, focalLengthY, tagSize, d.homography);
00445 
00446           // Compute the transformation for this detection
00447           Transform transform = TagPublisher.projectionMatrixToTransform(M);
00448 
00449           // Construct the TagPose message to transmit 
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           // Publish visualization for rviz
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           // Broadcast tf 
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         // Finally publish all everything for this frame
00486         publisher.publish(tagArray);
00487         if (publishVisualization) {
00488           visPublisher.publish(visArray);
00489         }
00490         if (broadcastTf) {
00491           tfPublisher.publish(tfMsg);
00492         }
00493       }
00494     }); /* end addMessageListener */
00495 
00496     // Subscribe to camera info message if necessary
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     } /* end useCameraInfo */
00523 
00524   }
00525 }


april_tags_node
Author(s): Piyush Khandelwal
autogenerated on Mon Jan 6 2014 11:54:27