package com.hackingroomba.roombacomm; import java.io.*; import java.net.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import javax.swing.*; import java.lang.Thread; import java.util.*; import java.lang.Math; public class FrameProcessor { private static final long serialVersionUID = 1L; private static final int lumBufHeight = 32; // socket variables int portNum = 5005; Socket videoSocket; BufferedInputStream in; BufferedOutputStream out; RoboRealmAPI rr; // video variables byte[] readBuf; // where we read raw network data into byte[] vidBuf; // raw pixel bytes int[] vidDispBuf, sliceBufInt; int[] lumBuf = new int[256*lumBufHeight]; int[] qDisplayBuf, q; // the quantized array, qDisplayBuf is video data, q is 0 or 1 in each int int[] ltBuf; // the graphic markers for current middle & bundaries of tracking String server = ""; int frameSize; int imgWidth; int imgHeight; Container contentPane; Image img, lumImg, quantizedImg, sliceImg, trackingImg; JFrame frame; Insets insets; int qStartRow, qEndRow; int thresholdOverride = 0; int frameCount = 0; // line variables. Units are pixels distance from center of image. Negative value is left of center class LineBoundary { int start; int middle; int end; }; ArrayList lineList; LineBoundary currentLine; int first; int[] smoothSlice; // a quantized slice across the image, averaged to remove noise int minLineThickness, maxLineThickness; public FrameProcessor(String server,int portnum, int width, int height, int minLine, int maxline, int thresholdOv) { imgWidth = width; imgHeight = height; minLineThickness = minLine; maxLineThickness = maxline; lineList = new ArrayList(); currentLine = new LineBoundary(); first = 1; smoothSlice = new int[width]; ltBuf = new int[4*imgWidth]; thresholdOverride = thresholdOv; this.server = server; this.portNum = portnum; } public void makeImagePane() { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } void createAndShowGUI() { //Create and set up the window. frame = new JFrame("Raw Image"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); contentPane = frame.getContentPane(); contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20)); //Display the window. frame.pack(); frame.setVisible(true); insets = frame.getInsets(); frame.setMinimumSize(new Dimension(400,350)); } public void displayFrame() { Graphics g = frame.getGraphics(); // display the raw image img = frame.createImage(new MemoryImageSource(imgWidth,imgHeight,vidDispBuf, 0, imgWidth)); g.drawImage(img, insets.left+2, insets.top+2, null); // display the slice sliceImg = frame.createImage(new MemoryImageSource(imgWidth, qEndRow-qStartRow, sliceBufInt, 0, imgWidth)); g.drawImage(sliceImg, insets.left, insets.top + 20 + imgHeight, null); // display the quantized image quantizedImg = frame.createImage(new MemoryImageSource(imgWidth, qEndRow-qStartRow, qDisplayBuf, 0, imgWidth)); g.drawImage(quantizedImg, insets.left, insets.top + 40 + imgHeight, null); // display the line tracking markers trackingImg = frame.createImage(new MemoryImageSource(imgWidth, 4, ltBuf, 0, imgWidth)); g.drawImage(trackingImg, insets.left, insets.top + 60 + imgHeight, null); //lumImg = frame.createImage(new MemoryImageSource(256,lumBufHeight,lumBuf, 0, 256)); //g.drawImage(lumImg, insets.left, insets.top + 20 + imgHeight, null); } public Boolean frame2Roborealm() { return rr.setImage(vidBuf, imgWidth, imgHeight); } public String getShapeData() { String rrString = rr.getVariable("SHAPES"); //System.out.println(rrString); return rrString; } public void connect(boolean connectRoborealm) { try { videoSocket = new Socket(server, portNum); in = new BufferedInputStream(videoSocket.getInputStream()); out = new BufferedOutputStream(videoSocket.getOutputStream()); } catch (UnknownHostException e) { System.out.println("Unknown host: " + server + ":" + portNum); System.exit(-1); } catch(IOException e) { System.out.println("I/O exception"); e.printStackTrace(); System.exit(-1); } System.out.println("connected to video"); // connect to RoboRealm if requested if (connectRoborealm) { rr = new RoboRealmAPI(); if (!rr.connect("localhost")) { System.out.println("Could not connect to RoboRealm on localhost! Exiting..."); System.exit(-1); } } } public void disconnect() { try { // do io streams need to be closed first? if (in != null) in.close(); } catch (Exception e) { e.printStackTrace(); } in = null; try { if (videoSocket != null) videoSocket.close(); } catch (Exception e) { e.printStackTrace(); } videoSocket = null; System.out.println("disconnected from Video"); } /** * Read a video frame from the network device * @param width in pixels * @param height in pixels * @param captureType 0 = monochrome, 1 = rgb color * @return number of pixels read or negative for error during read (e.g. size doesn't match frame size) */ public int readFrame(int width, int height, int captureType) { int bytesPerPixel = 1; int readLength = 0; int rxImageSize; int rgbShiftSize = 16; int pixel = 255 << 24; if (captureType == 1) // if rgb color, 3 bytes/pixel, otherwise 1 for grayscale bytesPerPixel = 3; frameSize = width * height * bytesPerPixel; int maxReadSize = frameSize; readBuf = new byte[frameSize]; // analysis form - temp buffer for raw received data vidBuf = new byte[frameSize]; // raw image bytes vidDispBuf = new int[frameSize]; // display version (alpha set, bytes replicated if needed) int vidDispBufIx = 0; int vidBufIx = 0; readBuf[0] = (byte)(200 + captureType); // send the "capture" command byte[] t = new byte[4]; try { out.write(readBuf, 0, 1); out.flush(); readLength = in.read(t, 0, 4); // read imgWidth if (readLength != 4) return -4; //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); imgWidth = ((t[2] << 8) & 0xff00) | t[3] & 0xff; // I think this way of getting byte to int is broken if (imgWidth != width) return -1; readLength = in.read(t, 0, 4); // read imgHeight //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); if (readLength != 4) return -5; imgHeight = (int)(t[2] & 0xff) << 8 | t[3] & 0xff; //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); if (imgHeight != height) return -2; readLength = in.read(t, 0, 4); // read imgLength if (readLength != 4) return -6; rxImageSize = ((t[1] << 16) & 0xff0000) | ((t[2] << 8) & 0xff00) | (int)t[3] & 0xff; //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); if (rxImageSize != frameSize) return -3; //System.out.println("Chumby reports image size " + imgWidth + "x" + imgHeight + " length " + rxImageSize + " for frameSize " + frameSize); // ACHTUNG - readBuf gets overwritten at the beginning by multiple read buffers - use vidDispBuf or vidBuf for image for (int i=0; i<50; i++) { readLength = in.read(readBuf, 0, maxReadSize); maxReadSize -= readLength; for (int j=0; j minLineThickness & create a LineBoundary for each blackStart = blackEnd = 0; for (int i=0; i minLineThickness) && ((blackEnd - blackStart) < maxLineThickness)) { LineBoundary lb = new LineBoundary(); lb.start = blackStart - (imgWidth/2); lb.end = blackEnd - (imgWidth/2); lb.middle = ((lb.start + lb.end)/2); lineList.add(lb); } // else ignore this as a false line (noise) - we're in white now } } else { // inWhite if (smoothSlice[i] == 0) { inBlack = true; // were in white, just transitioned to black (can happen on element 0) blackStart = i; } } } // print found lines // System.out.print("Found " + lineList.size() + " lines: "); // for (LineBoundary l : lineList) { // System.out.print(l.start + " " + l.middle + " " + l.end + " "); // } // System.out.println(); return(0); } /* * Find the line we should be following. The very first time this runs, or if it loses the line & backs up * it will pick the line closest to center. Thereafter it chooses the first found line who's center is within * the boundaries of the last line it chose. If it can't find one, it returns an error. Therefore it will always take * a left fork. It returns the middle value of the chosen line, or a large value if error. */ public int trackLine() { int m; LineBoundary lTmp = new LineBoundary(); // clear out current markers in the tracking display m = currentLine.middle + imgWidth/2; ltBuf[m + 3*imgWidth] = ltBuf[m + 2*imgWidth] = ltBuf[m + imgWidth] = ltBuf[m] = 0; if (lineList.size() == 0) { if (currentLine.middle >= 0) { System.out.println("Error: line disappeared to right, last seen at " + currentLine.middle); return(100); } else { System.out.println("Error: line disappeared to left, last seen at " + currentLine.middle); return (-100); } } if (first == 1) { lTmp.middle = 100; // any found line will be closer than this for (LineBoundary lb : lineList) { if (Math.abs(lb.middle) < Math.abs(lTmp.middle)) { lTmp = lb; // save the new lineBoundary with the lowest absolute value of middle } } currentLine = lTmp; first = 0; System.out.print("Picked line center at " + lTmp.middle); return(lTmp.middle); } else { if (lineList.size() == 1) { System.out.println("tracking line center at " + lineList.get(0).middle + " width: " + (lineList.get(0).end-lineList.get(0).start)); currentLine = lineList.get(0); return (lineList.get(0).middle); } for (LineBoundary lb : lineList) { if ((lb.middle > currentLine.start-20) && (lb.middle < currentLine.end + 20)) { System.out.println("tracking line center at " + lb.middle + " width: " + (lb.end-lb.start)); currentLine = lb; // write the image of the new currentLine middle m = lb.middle+(imgWidth/2); ltBuf[m] = 0xff<<24 | 0xff<<16; ltBuf[m + 3*imgWidth] = ltBuf[m + 2*imgWidth] = ltBuf[m + imgWidth] = ltBuf[m]; return (lb.middle); } } } System.out.println("Error: Lost the line I was tracking"); first = 1; return(2001); } public void testQuantization() { byte[] testArray1 = {6, 6, 6, 10, 6, 6, 6, 10, 17, 17, 17, 17, 17, 17, 17, 88}; for (int i=0; i