<repo_name>HelloNYC2016/Computer-Vision<file_sep>/Project 1/ImageProcess/src/Demo.java
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.awt.event.*;
import javax.swing.*;
import javax.imageio.*;
import javax.swing.event.*;

public class Demo extends JApplet {

	Image edgeDetectedImage, lineDetectedImage, parallelogramDetectedImage;
	MediaTracker tracker = null;
	PixelGrabber grabber = null;
	int width = 0, height = 0;
	String fileNames[] = { "TestImage1c.jpg", "TestImage2c.jpg", "TestImage3.jpg"};

	// slider constraints
	static final int TH_MIN = 0;
	static final int TH_MAX = 255;
	static final int TH_INIT = 40;
	int threshold = TH_INIT;
	int minimumLengthOfLines = 100;
	boolean thresholdActive = false;
	boolean isOverlaid = false;

	int imageNumber = 0;
	public int orig[] = null;

	Image image[] = new Image[fileNames.length];
	Image displayImage[] = new Image[fileNames.length];

	JPanel controlPanel, imagePanel;
	JLabel origLabel, edgeDetectedLabel, lineDetectedLabel, parallelogramDetectedLabel, comboLabel,
			minimumLengthOfLinesLabel;
	JSlider thresholdSlider, lengthSlider;
	JButton thresholding;
	JComboBox imSel;
	static Sobel edgedetector;
	static LineHough linedetector;
	static FindParallelogram parallelogramDetector;

	// Applet init function
	public void init() {

		tracker = new MediaTracker(this);
		for (int i = 0; i < fileNames.length; i++) {
			image[i] = getImage(this.getCodeBase(), fileNames[i]);
			tracker.addImage(image[i], i);
		}
		try {
			tracker.waitForAll();
		} catch (InterruptedException e) {
			System.out.println("error: " + e);
		}

		Container cont = getContentPane();
		cont.removeAll();
		cont.setLayout(new BorderLayout());

		controlPanel = new JPanel();
		controlPanel.setLayout(new GridLayout(2, 4, 15, 0));
		imagePanel = new JPanel();

		// row 1, col 1
		comboLabel = new JLabel("SELECT AN IMAGE");
		comboLabel.setHorizontalAlignment(JLabel.CENTER);
		controlPanel.add(comboLabel);

		// row 1, col 2
		thresholding = new JButton("Thresholding Off");
		thresholding.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (thresholdActive == true) {
					thresholdActive = false;
					thresholding.setText("Thresholding Off");
					thresholdSlider.setEnabled(false);
				} else {
					thresholdActive = true;
					thresholding.setText("Threshold Value = " + threshold);
					thresholdSlider.setEnabled(true);
				}
				processImage();
			}
		});
		controlPanel.add(thresholding);

		// row 1, col 3
		minimumLengthOfLinesLabel = new JLabel("Minimum Length of Line detected = " + minimumLengthOfLines);
		minimumLengthOfLinesLabel.setHorizontalAlignment(JLabel.CENTER);
		controlPanel.add(minimumLengthOfLinesLabel);

		// row 2, col 1
		imSel = new JComboBox(fileNames);
		imageNumber = imSel.getSelectedIndex();
		imSel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				imageNumber = imSel.getSelectedIndex();
				// change size
				width = image[imageNumber].getWidth(null);
				height = image[imageNumber].getHeight(null);
				origLabel.setIcon(
						new ImageIcon(image[imageNumber].getScaledInstance(width / 3, -1, Image.SCALE_SMOOTH)));
				processImage();
			}
		});
		controlPanel.add(imSel);

		// row 2, col 2
		thresholdSlider = new JSlider(JSlider.HORIZONTAL, TH_MIN, TH_MAX, TH_INIT);
		thresholdSlider.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				// TODO Auto-generated method stub
				JSlider source = (JSlider) e.getSource();
				if (!source.getValueIsAdjusting()) {
					threshold = source.getValue();
					thresholding.setText("Threshold Value = " + threshold);
					processImage();
				}
			}
		});
		thresholdSlider.setMajorTickSpacing(40);
		thresholdSlider.setMinorTickSpacing(10);
		thresholdSlider.setPaintTicks(true);
		thresholdSlider.setPaintLabels(true);
		controlPanel.add(thresholdSlider);

		// row 2, col 3
		lengthSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, minimumLengthOfLines);
		lengthSlider.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				// TODO Auto-generated method stub
				JSlider source = (JSlider) e.getSource();
				if (!source.getValueIsAdjusting()) {
					minimumLengthOfLines = source.getValue();
					minimumLengthOfLinesLabel.setText("Minimum Length of Line detected = " + minimumLengthOfLines);
					processImage();
				}
			}
		});
		lengthSlider.setMajorTickSpacing(40);
		lengthSlider.setMinorTickSpacing(10);
		lengthSlider.setPaintTicks(true);
		lengthSlider.setPaintLabels(true);
		controlPanel.add(lengthSlider);

		// default size
		width = image[imageNumber].getWidth(null);
		height = image[imageNumber].getHeight(null);

		origLabel = new JLabel("Original Image",
				new ImageIcon(image[imageNumber].getScaledInstance(width / 3, -1, Image.SCALE_SMOOTH)), JLabel.CENTER);
		origLabel.setVerticalTextPosition(JLabel.BOTTOM);
		origLabel.setHorizontalTextPosition(JLabel.CENTER);
		origLabel.setForeground(Color.blue);
		imagePanel.add(origLabel);

		edgeDetectedLabel = new JLabel("Edge Detected",
				new ImageIcon(image[imageNumber].getScaledInstance(width / 3, -1, Image.SCALE_SMOOTH)), JLabel.CENTER);
		edgeDetectedLabel.setVerticalTextPosition(JLabel.BOTTOM);
		edgeDetectedLabel.setHorizontalTextPosition(JLabel.CENTER);
		edgeDetectedLabel.setForeground(Color.blue);
		imagePanel.add(edgeDetectedLabel);

		lineDetectedLabel = new JLabel("Hough Line Image",
				new ImageIcon(image[imageNumber].getScaledInstance(width / 3, -1, Image.SCALE_SMOOTH)), JLabel.CENTER);
		lineDetectedLabel.setVerticalTextPosition(JLabel.BOTTOM);
		lineDetectedLabel.setHorizontalTextPosition(JLabel.CENTER);
		lineDetectedLabel.setForeground(Color.blue);
		imagePanel.add(lineDetectedLabel);
		 
		cont.add(controlPanel, BorderLayout.NORTH);
		cont.add(imagePanel, BorderLayout.CENTER);

		processImage();

	}

	public int[] threshold(int[] original, int value) {
		for (int x = 0; x < original.length; x++) {
			if ((original[x] & 0xff) >= value)
				// foreground is set to black
				original[x] = 0xff000000;
			else
				// background is set to white
				original[x] = 0xffffffff;
		}
		return original;
	}

	private int[] overlayImage(int[] input) {

		int[] myImage = new int[width * height];

		PixelGrabber grabber = new PixelGrabber(image[imageNumber], 0, 0, width, height, myImage, 0, width);
		try {
			grabber.grabPixels();
		} catch (InterruptedException e2) {
			System.out.println("error: " + e2);
		}

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				if ((input[y * width + x] & 0xff) > 0)
					myImage[y * width + x] = 0xffff0000;
			}
		}

		return myImage;

	}

	private void processImage() {
		orig = new int[width * height];
		PixelGrabber grabber = new PixelGrabber(image[imageNumber], 0, 0, width, height, orig, 0, width);
		try {
			grabber.grabPixels();
		} catch (InterruptedException e2) {
			System.out.println("error: " + e2);
		}

		thresholdSlider.setEnabled(false);
		thresholding.setEnabled(false);
		imSel.setEnabled(false);
		edgeDetectedLabel.setEnabled(false);
		lengthSlider.setEnabled(false);

		edgedetector = new Sobel();
		linedetector = new LineHough();

		new Thread() {
			public void run() {
				// detect edge
				edgedetector.init(orig, width, height);
				orig = edgedetector.process();
				if (thresholdActive == true)
					orig = threshold(orig, threshold);
				edgeDetectedImage = createImage(new MemoryImageSource(width, height, orig, 0, width));

				// detect line
				if (thresholdActive == true) {
					linedetector.init(orig, width, height);
					linedetector.setLength(minimumLengthOfLines);
					orig = linedetector.process();
					lineDetectedImage = createImage(new MemoryImageSource(width, height, overlayImage(orig), 0, width));
				} else {
					lineDetectedImage = createImage(new MemoryImageSource(width, height, orig, 0, width));
				}

				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						edgeDetectedLabel.setIcon(
								new ImageIcon(edgeDetectedImage.getScaledInstance(width / 3, -1, Image.SCALE_SMOOTH)));
						lineDetectedLabel.setIcon(
								new ImageIcon(lineDetectedImage.getScaledInstance(width / 3, -1, Image.SCALE_SMOOTH)));
						if (thresholdActive == true) {
							thresholdSlider.setEnabled(true);
						}
						thresholding.setEnabled(true);
						imSel.setEnabled(true);
						edgeDetectedLabel.setEnabled(true);
						lengthSlider.setEnabled(true);
					}
				});

				// write image to file
				edgeDetectedLabel.addMouseListener(new MouseAdapter() {
					public void mouseClicked(MouseEvent event) {
						try {
							// Create a buffered image with transparency
							BufferedImage bimage = new BufferedImage(edgeDetectedImage.getWidth(null),
									edgeDetectedImage.getHeight(null), BufferedImage.TYPE_BYTE_GRAY);
							// Draw the image on to the buffered image
							Graphics2D bGr = bimage.createGraphics();
							bGr.drawImage(edgeDetectedImage, 0, 0, null);
							bGr.dispose();

							String filename = fileNames[imSel.getSelectedIndex()];
							filename = filename.substring(0, filename.lastIndexOf('.'));
							String path;
							if (thresholdActive == true) {
								path = String.format("/Users/shingshing/desktop/result/%s_T=%d.jpg", filename,
										threshold);
							} else {
								path = String.format("/Users/shingshing/desktop/result/%s_edge_detected.jpg", filename);
							}
							ImageIO.write(bimage, "jpg", new File(path));
						} catch (IOException e) {
							System.out.println(e);
						}
					}
				});

			}
		}.start();
	}
}<file_sep>/Project 1/ImageProcess/src/FindParallelogram.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import data.Line;
import data.Parallelogram;
import data.Point;

public class FindParallelogram {

	int width;
	int height;
	List<Line> lines;
	List<Parallelogram> parallelograms;
	Map<Point, Integer> points;
	
	
	public FindParallelogram(int width, int height, List<Line> lines) {
		super();
		this.width = width;
		this.height = height;
		this.lines = lines;
	}
	
	public void findParallelogram() {
		//HashMap is used to reduce duplicate find
		points = new HashMap<>();
		List<Parallelogram> parallelograms = new ArrayList<>();
		//For each line we find with Hough Transform
		for (int i = 0; i < lines.size(); i++) {
			Line line = lines.get(i);
			List<Line> parallels = findParallel(line);
			List<Line> crossLines = findCrossLine(line);
			for (Line parallel : parallels) {
				for (Line crossLine : crossLines) {
					Point p1 = findIntersection(crossLine, line);
					Point p2 = findIntersection(crossLine, parallel);
					//The intersection has to be valid and not already been added to the map
					if (isPointValid(p1) && isPointValid(p2) && !points.containsKey(p1) && !points.containsKey(p2)) {
						points.put(p1, 1);
						points.put(p2, 1);
						List<Line> parallelsOfCrossLine = findParallel(crossLine);
						for (Line parallelOfCrossLine : parallelsOfCrossLine) {
							Point p3 = findIntersection(parallelOfCrossLine, line);
							Point p4 = findIntersection(parallelOfCrossLine, parallel);
							if (isPointValid(p3) && isPointValid(p4) && !points.containsKey(p3) && !points.containsKey(p4)) {
								parallelograms.add(new Parallelogram(p1, p2, p3, p4));
								points.put(p3, 1);
								points.put(p4, 1);
							}
						}
					}	
				}
			}
		}
		for (Parallelogram parallelogram : parallelograms) {
			System.out.println(parallelogram.toString());
		}	
	}
	
	
	private Point findIntersection(Line line1, Line line2) {
		int angle = Math.abs(line1.angle - line2.angle);
		//We don't find intersections for two lines that are almost parallel
		if (angle > 10 && angle < 350) {
			int r1 = line1.r, r2 = line2.r;
			int a1 = line1.angle, a2 = line2.angle;
			int x = (int) ((r2*Math.sin(((a1)*Math.PI)/180) - r1*Math.sin(((a2)*Math.PI)/180))
					/(Math.cos(((a2)*Math.PI)/180) * Math.sin(((a1)*Math.PI)/180) - Math.cos(((a1)*Math.PI)/180)*Math.sin(((a2)*Math.PI)/180)));
			int y = (int) ((r2*Math.cos(((a1)*Math.PI)/180) - r1*Math.cos(((a2)*Math.PI)/180))
					/(Math.cos(((a1)*Math.PI)/180) * Math.sin(((a2)*Math.PI)/180) - Math.cos(((a2)*Math.PI)/180)*Math.sin(((a1)*Math.PI)/180)));
			Point point = new Point(x, y);
			return point;
		} else {
			return new Point(-1, -1);
		}
		
	}
	
	private boolean isPointValid(Point point) {
		//intersection must me in the image
		if (point.x < width && point.x >= 0 && point.y >= 0 && point.y < height) return true;
		else return false;
	}
	
	//If two lines almost coincide, they are not regarded as parallel here
	//There should be some distance between two parallel lines, the threshold here is 50
	private List<Line> findParallel(Line line) {
		List<Line> parallels = new ArrayList<>();
		for (int i = 0; i < lines.size(); i++) {
			int diff = Math.abs(line.angle - lines.get(i).angle); 
			if (((diff >= 0 && diff <= 4) || (diff <= 182 && diff >= 178)) 
					&& Math.abs(line.r - lines.get(i).r) > 50)
				parallels.add(lines.get(i));
		}
		return parallels;
	}
	
	private List<Line> findCrossLine(Line line) {
		List<Line> crossLines = new ArrayList<>();
		for (int i = 0; i < lines.size(); i++) {
			Point intersection = findIntersection(line, lines.get(i));
			if (isPointValid(intersection)) {
				crossLines.add(lines.get(i));
			}
		}
		return crossLines;
	}
	
}
<file_sep>/README.md
# Computer-Vision

Project 1: Parallelograms Detection

Project 2: Face Recognition
<file_sep>/Project 1/ImageProcess/src/LineHough.java
import java.lang.Math;
import java.util.ArrayList;
import java.util.List;

import data.Line;

public class LineHough {
	int[] input;
	int[] output;
	int width;
	int height;
	int[] acc;
	int minimumLengthOfLine;
	List<Line> results;
	FindParallelogram parallelogramDetector;
	
	public void init(int[] inputIn, int widthIn, int heightIn) {
		width = widthIn;
		height = heightIn;
		input = new int[width * height];
		output = new int[width * height];
		input = inputIn;
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				output[y * width + x] = 0xff000000;
			}
		}
	}

	public void setLength(int len) {
		minimumLengthOfLine = len;
	}

	// hough transform for lines (polar), returns the accumulator array
	public int[] process() {
		// for polar we need accumulator of 360 degress * the longest length in
		// the image
		int rmax = (int) Math.sqrt(width * width + height * height);
		acc = new int[rmax * 360];
		int r;

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				// foreground 0xff000000
				if ((input[y * width + x] & 0xff) == 0) {
					for (int angle = 0; angle < 360; angle++) {
						r = (int) (x * Math.cos(((angle) * Math.PI) / 180) + y * Math.sin(((angle) * Math.PI) / 180));
						if ((r > 0) && (r <= rmax))
							acc[r * 360 + angle] = acc[r * 360 + angle] + 1;
					}
				}
			}
		}

		// now normalise to 255 and put in format for a pixel array
		int max = 0;

		// Find max acc value
		for (r = 0; r < rmax; r++) {
			for (int angle = 0; angle < 360; angle++) {
				if (acc[r * 360 + angle] > max) {
					//System.out.println("r =" + r + ", theta = " + angle * 3 + 1);
					max = acc[r * 360 + angle];
				}
			}
		}

		// System.out.println("Max :" + max);

		// Normalise all the values
		int value;
		for (r = 0; r < rmax; r++) {
			for (int angle = 0; angle < 360; angle++) {
				value = (int) (((double) acc[r * 360 + angle] / (double) max) * 255.0);
				acc[r * 360 + angle] = 0xff000000 | (value << 16 | value << 8 | value);
			}
		}

		threshold();
		
		//find parallelograms
		parallelogramDetector = new FindParallelogram(width, height, new ArrayList<Line>(results));
		parallelogramDetector.findParallelogram();
		
		return output;
	}

	private void threshold() {
		// for polar we need accumulator of 360degress * the longest length in
		// the image
		int rmax = (int) Math.sqrt(width * width + height * height);
		results = new ArrayList<>();

		for (int r = 0; r < rmax; r++) {
			for (int angle = 0; angle < 360; angle++) {
				int value = (acc[r * 360 + angle] & 0xff);
				// if its higher than the threshold add it
				if (value >= minimumLengthOfLine) {
					Line line = new Line(value, r, angle);
					results.add(line);
				}
			}
		}

		for (Line line : results) {
			drawPolarLine(line.value, line.r, line.angle);
		}
	}

	// draw a line given polar coordinates (and an input image to allow drawing
	// more than one line)
	private void drawPolarLine(int value, int r, int angle) {
		for (int x = 0; x < width; x++) {

			for (int y = 0; y < height; y++) {

				int temp = (int) (x * Math.cos(((angle) * Math.PI) / 180) + y * Math.sin(((angle) * Math.PI) / 180));
				if ((temp - r) == 0)
					output[y * width + x] = 0xff000000 | (value << 16 | value << 8 | value);

			}
		}
	}

}