1

The below code is supposed to detect an image, create a 2d array containing the pixel values from that image, and determine the path of lowest cost (I used Uniform Cost Search) from a Point A inside the image to a Point B inside the image. When I run my code, I get the following error:

Exception in thread "main" java.lang.ClassCastException: UniformCostSearch$Node
cannot be cast to java.lang.Comparable
        at java.util.PriorityQueue.siftUpComparable(Unknown Source)
        at java.util.PriorityQueue.siftUp(Unknown Source)
        at java.util.PriorityQueue.offer(Unknown Source)
        at java.util.PriorityQueue.add(Unknown Source)
        at UniformCostSearch.uCostSearch(UniformCostSearch.java:159)
        at UniformCostSearch.main(UniformCostSearch.java:216)

Why does this occur, and how can I fix this? I've viewed other solutions, but I believe I have a different issue.

public class UniformCostSearch
{   
    //------------------------------------
    // Node Class
    //------------------------------------
    public static class Node implements Comparator<Node>
    {
        public double cost;
        Node parent;
        public int x; 
        public int y;

        public Node(){}

        public Node(double cost, Node parent, int x, int y)
        {
            this.cost = cost;
            this.parent = parent;
            this.x = x;
            this.y = y;
        }

        @Override
        public int compare(Node node1, Node node2)
        {
            if(node1.cost < node2.cost)
                return -1;
            if(node1.cost > node2.cost)
                return 1;
            return 0;
        }

        @Override
        public boolean equals(Object obj)
        {
            if(obj instanceof Node)
            {
                Node node = (Node) obj;
                if (this.parent == node.parent)
                    return true;
            }
            return false;
        }
    }

    public static void uCostSearch(Node startState, Node endNode, 
        BufferedImage img){

        int adjacencyMatrix[][] = null;
        PriorityQueue<Node> pq = new PriorityQueue<Node>();
        startState.cost = 0.0;
        startState.parent = null;
        int inc = 0;

        pq.add(startState);

        Set<Point> visited = new HashSet<Point>();
        Point startPoint = new Point(startState.x, startState.y);
        visited.add(startPoint);

        byte[] terrain =
            ((DataBufferByte)img.getRaster().getDataBuffer()).getData();
        int width = img.getWidth();
        int height = img.getHeight();
        final boolean hasAlphaChannel = img.getAlphaRaster() != null;
        int channel_count = 4;
        int green_channel = 2; // 0=alpha, 1=blue, 2=green, 3=red

        int[][] result = new int[height][width];

// Source: http://stackoverflow.com/questions/6524196/java-get-pixel-array-from-image
         if (hasAlphaChannel) {
         final int pixelLength = 4;
         for (int pixel = 0, row = 0, col = 0; pixel < terrain.length; pixel += pixelLength) {
            int argb = 0;
            argb += (((int) terrain[pixel] & 0xff) << 24); // alpha
            argb += ((int) terrain[pixel + 1] & 0xff); // blue
            argb += (((int) terrain[pixel + 2] & 0xff) << 8); // green
            argb += (((int) terrain[pixel + 3] & 0xff) << 16); // red
            result[row][col] = argb;
            col++;
            if (col == width) {
               col = 0;
               row++;
            } 
         } 
      } else { 
         final int pixelLength = 3;
         for (int pixel = 0, row = 0, col = 0; pixel < terrain.length; pixel += pixelLength) {
            int argb = 0;
            argb += -16777216; // 255 alpha
            argb += ((int) terrain[pixel] & 0xff); // blue
            argb += (((int) terrain[pixel + 1] & 0xff) << 8); // green
            argb += (((int) terrain[pixel + 2] & 0xff) << 16); // red
            result[row][col] = argb;
            col++;
            if (col == width) {
               col = 0;
               row++;
            } 
         }
      }

        while(!pq.isEmpty()){
            Node s = pq.remove();
            if(s == endNode)
                return;
            else{
                if(inc++ % 5000 < 1000){
                    // set to Green
                    img.setRGB(s.y, s.x, 0xFF00FF00);       
                }
            ArrayList<Point> directions = new ArrayList<Point>();
            directions.add(new Point(s.x, s.y+1));
            directions.add(new Point(s.x, s.y-1));
            directions.add(new Point(s.x+1, s.y));
            directions.add(new Point(s.x-1, s.y));
            for(Point step: directions){
                int x = step.x;
                int y = step.y;

                if(0 <= y && y < height && 0 <= x && x < width 
                    && !(visited.contains(step))){
                        visited.add(step);
                        Node temp = new Node(s.cost + channel_count * 
                        (y * width + x) + green_channel, s, x, y);
                        pq.add(temp);   
                    }
            }
            }
            inc++;
            System.out.print(inc);
        }       
    }

    public static BufferedImage GetImage(String filename)
    {
        BufferedImage bImage = null;
        try{
            bImage = ImageIO.read(new File(filename));
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return bImage;
    }

    /*
    public createPath(Node node, BufferedImage image){
        while(node.parent != null){
            //set pixel to red;
            node = node.parent;
        }
    }
    */

    // main method
    public static void main(String[] args)
    {
        int startX = 0, startY = 0, endX = 0, endY = 0;
        BufferedImage bufferedImage = null;
        // Get filename and start and end points from command line
        try{
            String filename = args[0];
            startX = Integer.parseInt(args[1]);
            startY = Integer.parseInt(args[2]);
            endX = Integer.parseInt(args[3]);
            endY = Integer.parseInt(args[4]);
            bufferedImage = GetImage(filename);
            }
            catch(Exception e)
            {
                e.printStackTrace();
                System.out.print("Please enter filename start.x start.y" + 
                    " end.x end.y in that order");
            }

            Node startingPoint = new Node(0.0, null, startX, startY);
            Node endingPoint = new Node();
            endingPoint.x = endX;
            endingPoint.y = endY;

            uCostSearch(startingPoint, endingPoint, bufferedImage);

            //createPath(endingPoint, bufferedImage)
            //saveImage
            //PrintCost
            //Output Image

    }

}
Kai Tribble
  • 208
  • 1
  • 10

1 Answers1

1

To satisfy the sorting capability of the PriorityQueue class (used by UniformCostSearch), your Node class must implement the Comparable interface, not the Comparator interface.

To use the node parent in your comparison, you might try this:

    @Override
    public int compareTo(Node that)
    {
        if (this.parent != null && that.parent != null) {
            return this.parent.compareTo(that.parent);
        }
        return this.cost - that.cost;
    }

That's just an example though. I make no claim it will fill your requirements, but it will prevent NullPointerException.

gknicker
  • 5,509
  • 2
  • 25
  • 41
  • Thank you. Will update first thing tomorrow and share results. – Kai Tribble Feb 01 '15 at 07:44
  • I'm using Comparable now, which solved my ClassCastException. However, there is a null pointer exception thrown when I include it. Here is my code for compareTo: @Override public int compareTo(Node node) { return this.parent.compareTo(node.parent); } – Kai Tribble Feb 01 '15 at 17:27
  • It's possible `this.parent.compareTo()` will recurse up the tree until it reaches the root node, which I assume has no parent (null), at which point calling `compareTo()` on the null parent yields NullPointerException. Also, I'm sure you will discover that comparing by parent alone is inadequate to meet the contract of `Comparable.compareTo()`, so you will likely need a secondary comparison criterion based on some attribute of the nodes being compared. – gknicker Feb 01 '15 at 20:39
  • I modified it to: this.parent.compareTo(node); and I'm still getting the NPE. – Kai Tribble Feb 02 '15 at 19:33
  • 1
    Same thing bro - that statement recurses to the top of your tree structure and then tries to call `compareTo()` on a the null parent at the top level. See my edit. – gknicker Feb 02 '15 at 23:14