task 1
| @@ -8,5 +8,8 @@ | |||||||
|     <orderEntry type="inheritedJdk" /> |     <orderEntry type="inheritedJdk" /> | ||||||
|     <orderEntry type="sourceFolder" forTests="false" /> |     <orderEntry type="sourceFolder" forTests="false" /> | ||||||
|     <orderEntry type="library" name="scala-sdk-2.13.12" level="project" /> |     <orderEntry type="library" name="scala-sdk-2.13.12" level="project" /> | ||||||
|  |     <orderEntry type="library" name="bridj-0.7.0" level="project" /> | ||||||
|  |     <orderEntry type="library" name="slf4j-api-1.7.2" level="project" /> | ||||||
|  |     <orderEntry type="library" name="webcam-capture-0.3.12" level="project" /> | ||||||
|   </component> |   </component> | ||||||
| </module> | </module> | ||||||
							
								
								
									
										
											BIN
										
									
								
								libs/bridj-0.7.0.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								libs/slf4j-api-1.7.2.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								libs/webcam-capture-0.3.12.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								res/Dead_tree.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 94 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/collins_eileen.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 285 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/grace_hopper.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 60 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/imageProcessing.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 140 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/imageProcessing_empty.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/mask.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 23 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/mask640x480.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 19 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/moire1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 279 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/moire2.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 19 KiB | 
							
								
								
									
										
											BIN
										
									
								
								res/rice.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 14 KiB | 
							
								
								
									
										13
									
								
								src/imagefilters/ImageFilters.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | |||||||
|  | package imagefilters | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This class implements the various image filters | ||||||
|  |  */ | ||||||
|  | object ImageFilters { | ||||||
|  |  | ||||||
|  |   def duplicate(a: Array[Array[Int]]): Array[Array[Int]] = { | ||||||
|  |  | ||||||
|  |     /* TODO: Write your code hereunder */ | ||||||
|  |     return Array.empty | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								src/imagefilters/ImageProcessingApp.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,14 @@ | |||||||
|  | package imagefilters | ||||||
|  |  | ||||||
|  | import isc.graphics.ImageGraphics | ||||||
|  |  | ||||||
|  | object ImageProcessingApp extends App { | ||||||
|  |  | ||||||
|  | 	val imageFile = "./res/collins_eileen.png" | ||||||
|  | 	val org = new ImageGraphics(imageFile, "Original", -500, -250) | ||||||
|  | 	val dest = new ImageGraphics(imageFile, "Duplicate", 0, -250) | ||||||
|  |  | ||||||
|  |     // Simple copy and display | ||||||
|  | 	val newPixels = ImageFilters.duplicate(org.getPixelsBW()) | ||||||
|  | 	dest.setPixelsBW(newPixels) | ||||||
|  | } | ||||||
							
								
								
									
										241
									
								
								src/isc/graphics/ImageGraphics.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,241 @@ | |||||||
|  | package isc.graphics | ||||||
|  |  | ||||||
|  | import java.awt._ | ||||||
|  | import java.awt.image.BufferedImage | ||||||
|  | import java.io.{File, FileInputStream} | ||||||
|  | import javax.imageio.ImageIO | ||||||
|  | import javax.swing.{JFrame, JPanel} | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * [ImageGraphics] helpers functions in companion object. | ||||||
|  |  */ | ||||||
|  | object ImageGraphics { | ||||||
|  | 	/** | ||||||
|  | 	 * Converts a color array to a black-or-white array | ||||||
|  | 	 * | ||||||
|  | 	 * @param c The color array | ||||||
|  | 	 * @return The array converted to BW | ||||||
|  | 	 */ | ||||||
|  | 	def convertToGray(c: Array[Array[Color]]): Array[Array[Color]] = { | ||||||
|  | 		val w = c.length | ||||||
|  | 		val h = c(0).length | ||||||
|  | 		val values = Array.ofDim[Color](w, h) | ||||||
|  |  | ||||||
|  | 		// FIXME this is slow | ||||||
|  | 		for (i <- 0 until w) { | ||||||
|  | 			for (j <- 0 until h) { | ||||||
|  | 				val col = c(i)(j) | ||||||
|  | 				val intColor = (0.3 * col.getRed + 0.59 * col.getGreen + 0.11 * col.getBlue).toInt | ||||||
|  | 				values(i)(j) = new Color(intColor, intColor, intColor) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		values | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	def convertToGrayInt(c: Array[Array[Color]]): Array[Array[Int]] = { | ||||||
|  | 		val w = c.length | ||||||
|  | 		val h = c(0).length | ||||||
|  | 		val values = Array.ofDim[Int](w, h) | ||||||
|  | 		for (i <- 0 until w) { | ||||||
|  | 			for (j <- 0 until h) { | ||||||
|  | 				val col = c(i)(j) | ||||||
|  | 				val intColor = (0.3 * col.getRed + 0.59 * col.getGreen + 0.11 * col.getBlue).toInt | ||||||
|  | 				values(i)(j) = intColor | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		values | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	def main(args: Array[String]): Unit = { | ||||||
|  | 		val imageUsed = "res/grace_hopper.jpg" | ||||||
|  | 		val org = new ImageGraphics(imageUsed, "Original", 0, 0) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This class was made to deal with images as multidimensional arrays. | ||||||
|  |  * Mainly used in the <code>ImageProcessing</code> lab. It expects the images to reside in the <code>src</code> directory | ||||||
|  |  * | ||||||
|  |  * @author Pierre-André Mudry | ||||||
|  |  * @version 1.0 | ||||||
|  |  * @constructor | ||||||
|  |  * | ||||||
|  |  * @param filePath        the path of the file, relative to the project. For instance, "./res/grace_hopper.jpg" | ||||||
|  |  * @param windowTitle     the title | ||||||
|  |  * @param xPositionOffset the x offset | ||||||
|  |  * @param yPositionOffset the y offet | ||||||
|  |  */ | ||||||
|  | class ImageGraphics(val filePath: String, val windowTitle: String, | ||||||
|  | 						  val xPositionOffset: Int, val yPositionOffset: Int) extends JFrame { | ||||||
|  |  | ||||||
|  | 	private var imgBuffer: BufferedImage = null | ||||||
|  | 	private var w = 0 | ||||||
|  | 	private var h = 0 | ||||||
|  |  | ||||||
|  | 	class ImgPanel extends JPanel { | ||||||
|  | 		override def paint(g: Graphics): Unit = { | ||||||
|  | 			super.paint(g) | ||||||
|  | 			g.drawImage(imgBuffer, 0, 0, null) | ||||||
|  | 			g.dispose() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	val imgPanel = new ImgPanel() | ||||||
|  |  | ||||||
|  | 	try { // Fill the frame content with the image | ||||||
|  | 		try { | ||||||
|  | 			imgBuffer = ImageIO.read(new FileInputStream(new File(filePath))) | ||||||
|  | 			w = imgBuffer.getWidth | ||||||
|  | 			h = imgBuffer.getHeight | ||||||
|  | 		} catch { | ||||||
|  | 			case e: Exception => | ||||||
|  | 				println("Could not load directly, using classpath.") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// If the first technique failed, try using resource instead | ||||||
|  | 		if (imgBuffer == null) { | ||||||
|  | 			try { | ||||||
|  | 				imgBuffer = ImageIO.read(classOf[ImageGraphics].getResource(filePath)) | ||||||
|  | 				w = imgBuffer.getWidth | ||||||
|  | 				h = imgBuffer.getHeight | ||||||
|  | 			} catch { | ||||||
|  | 				case e: Exception => | ||||||
|  | 					System.err.println("Could not find image " + filePath + ", exiting !") | ||||||
|  | 					e.printStackTrace() | ||||||
|  | 					System.exit(-1) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		this.setResizable(false) | ||||||
|  | 		this.setTitle(windowTitle) | ||||||
|  | 		this.setLayout(new BorderLayout()) | ||||||
|  | 		this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE) | ||||||
|  |  | ||||||
|  | 		imgPanel.setPreferredSize(new Dimension(imgBuffer.getWidth, imgBuffer.getHeight)) | ||||||
|  | 		this.add(imgPanel, BorderLayout.CENTER) | ||||||
|  | 		this.pack() | ||||||
|  |  | ||||||
|  | 		// Get the size of the screen | ||||||
|  | 		val dim = Toolkit.getDefaultToolkit.getScreenSize | ||||||
|  |  | ||||||
|  | 		// Determine the new location of the window | ||||||
|  | 		val lw = this.getSize.width | ||||||
|  | 		val lh = this.getSize.height | ||||||
|  | 		val x = (dim.width - lw) / 2 + xPositionOffset | ||||||
|  | 		val y = (dim.height - lh) / 2 + yPositionOffset | ||||||
|  |  | ||||||
|  | 		// Move the window | ||||||
|  | 		this.setLocation(x, y) | ||||||
|  | 		this.setVisible(true) | ||||||
|  | 	} catch { | ||||||
|  | 		case e: Exception => | ||||||
|  | 			e.printStackTrace() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Sets a grayscale pixel, does not sets values for invalid pixels | ||||||
|  | 	 * outside the screen. Does not repaint the screen either because it | ||||||
|  | 	 * is slow. | ||||||
|  | 	 * | ||||||
|  | 	 * @param x | ||||||
|  | 	 * @param y | ||||||
|  | 	 * @param intensity | ||||||
|  | 	 */ | ||||||
|  | 	def setPixelBW(x: Int, y: Int, intensity: Int): Unit = { | ||||||
|  | 		if (!((x < 0) || (y < 0) || (x >= w) || (y >= h))) | ||||||
|  | 			imgBuffer.setRGB(x, y, intensity << 16 | intensity << 8 | intensity) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Sets an array of grayscale pixels (from 0 to 255) and displays them | ||||||
|  | 	 * | ||||||
|  | 	 * @param pixels | ||||||
|  | 	 */ | ||||||
|  | 	def setPixelsBW(pixels: Array[Array[Int]]): Unit = { | ||||||
|  | 		try { | ||||||
|  | 			if (pixels(0).length != h || pixels.length != w) throw new Exception("Invalid size of the pixel array !") | ||||||
|  | 			for (i <- 0 until w) { | ||||||
|  | 				for (j <- 0 until h) { | ||||||
|  | 					val c = pixels(i)(j) << 16 | pixels(i)(j) << 8 | pixels(i)(j) | ||||||
|  | 					imgBuffer.setRGB(i, j, c) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			this.repaint() | ||||||
|  | 		} catch { | ||||||
|  | 			case e: Exception => | ||||||
|  | 				e.printStackTrace() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Sets an array of pixels of Color and displays them | ||||||
|  | 	 * | ||||||
|  | 	 * @param pixels | ||||||
|  | 	 */ | ||||||
|  | 	def setPixelsColor(pixels: Array[Array[Color]]): Unit = { | ||||||
|  | 		try { | ||||||
|  | 			if (pixels(0).length != h || pixels.length != w) throw new Exception("Invalid size of the pixel array !") | ||||||
|  |  | ||||||
|  | 			for (i <- 0 until w) { | ||||||
|  | 				for (j <- 0 until h) { | ||||||
|  | 					imgBuffer.setRGB(i, j, pixels(i)(j).getRGB) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			this.repaint() | ||||||
|  | 		} catch { | ||||||
|  | 			case e: Exception => | ||||||
|  | 				e.printStackTrace() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets a single pixel from the background image and returns its | ||||||
|  | 	 * grayscale value | ||||||
|  | 	 * | ||||||
|  | 	 * @param x the x coordinate | ||||||
|  | 	 * @param y the y coordinate | ||||||
|  | 	 * @return the pixel | ||||||
|  | 	 */ | ||||||
|  | 	def getPixelBW(x: Int, y: Int): Int = | ||||||
|  | 		if ((x < 0) || (y < 0) || (x >= w) || (y >= h)) { | ||||||
|  | 			0 | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			// Inside the image. Make the gray conversion and return the value | ||||||
|  | 			val c = new Color(imgBuffer.getRGB(x, y)) | ||||||
|  | 			(0.3 * c.getRed + 0.59 * c.getGreen + 0.11 * c.getBlue).toInt | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets the array of the pixels (which have been converted to grayscale | ||||||
|  | 	 * if required) | ||||||
|  | 	 * | ||||||
|  | 	 * @return The arrays of gray pixels | ||||||
|  | 	 */ | ||||||
|  | 	def getPixelsBW(): Array[Array[Int]] = { | ||||||
|  | 		val values = Array.ofDim[Int](w, h) | ||||||
|  |  | ||||||
|  | 		for (i <- 0 until w) { | ||||||
|  | 			for (j <- 0 until h) { | ||||||
|  | 				val c = new Color(imgBuffer.getRGB(i, j)) | ||||||
|  | 				values(i)(j) = (0.3 * c.getRed + 0.59 * c.getGreen + 0.11 * c.getBlue).toInt | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		values | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets the array of the pixels as Colors (see #Color) | ||||||
|  | 	 * | ||||||
|  | 	 * @return The arrays of pixels | ||||||
|  | 	 */ | ||||||
|  | 	def getPixelsColor(): Array[Array[Color]] = { | ||||||
|  | 		val values = Array.ofDim[Color](w, h) | ||||||
|  | 		for (i <- 0 until w) { | ||||||
|  | 			for (j <- 0 until h) { | ||||||
|  | 				values(i)(j) = new Color(imgBuffer.getRGB(i, j)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		values | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										95
									
								
								src/livefilter/LiveFilter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,95 @@ | |||||||
|  | package livefilter | ||||||
|  |  | ||||||
|  | import com.github.sarxos.webcam.WebcamPanel.DrawMode | ||||||
|  | import com.github.sarxos.webcam.{Webcam, WebcamImageTransformer, WebcamPanel, WebcamResolution} | ||||||
|  |  | ||||||
|  | import java.awt.image.BufferedImage | ||||||
|  | import java.awt.{Color, Dimension} | ||||||
|  | import java.io.{File, FileInputStream} | ||||||
|  | import javax.imageio.ImageIO | ||||||
|  | import javax.swing.JFrame | ||||||
|  |  | ||||||
|  | object LiveFilter { | ||||||
|  | 	def main(args: Array[String]): Unit = { | ||||||
|  | 		new LiveFilter() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * A live filter class using VGA resolution | ||||||
|  |  */ | ||||||
|  | class LiveFilter extends WebcamImageTransformer { | ||||||
|  | 	val size: Dimension = WebcamResolution.VGA.getSize | ||||||
|  |  | ||||||
|  | 	val MASK: String = "./res/mask640x480.png" | ||||||
|  | 	val videoMask: Array[Array[Int]] = toBW(ImageIO.read(new FileInputStream(new File(MASK)))) | ||||||
|  |  | ||||||
|  | 	// Gets default webcam and sets image transformer to this (transformer will modify | ||||||
|  | 	// image after it's been received from webcam, in this case it will rotate it) | ||||||
|  | 	val webcam: Webcam = Webcam.getDefault | ||||||
|  |  | ||||||
|  | 	//val webcam: Webcam = Webcam.getWebcamByName("/dev/video2") | ||||||
|  |  | ||||||
|  | 	webcam.setViewSize(size) | ||||||
|  | 	println(size) | ||||||
|  | 	webcam.setImageTransformer(this) | ||||||
|  | 	webcam.open | ||||||
|  |  | ||||||
|  | 	// Create a window | ||||||
|  | 	val window = new JFrame("Test filter") | ||||||
|  |  | ||||||
|  | 	// Creates a panel to put the webcam image in it | ||||||
|  | 	val panel = new WebcamPanel(webcam) | ||||||
|  | 	panel.setFPSDisplayed(true) | ||||||
|  | 	panel.setDrawMode(DrawMode.FIT) | ||||||
|  |  | ||||||
|  | 	// Add panel to window | ||||||
|  | 	window.add(panel) | ||||||
|  | 	window.pack() | ||||||
|  | 	window.setVisible(true) | ||||||
|  |  | ||||||
|  | 	window.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE) | ||||||
|  |  | ||||||
|  | 	def toBW(img: BufferedImage): Array[Array[Int]] = { | ||||||
|  | 		val o = Array.ofDim[Int](img.getWidth(), img.getHeight()) | ||||||
|  | 		for (x <- o.indices) { | ||||||
|  | 			for (y <- o(0).indices) { | ||||||
|  | 				val c = new Color(img.getRGB(x, y)) | ||||||
|  | 				o(x)(y) = (0.3 * c.getRed + 0.59 * c.getGreen + 0.11 * c.getBlue).toInt | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		o | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	def toArray(image: BufferedImage): Array[Array[Color]] = { | ||||||
|  | 		val out = Array.ofDim[Color](image.getWidth, image.getHeight) | ||||||
|  |  | ||||||
|  | 		for (x <- out.indices; y <- out(0).indices) { | ||||||
|  | 			out(x)(y) = new Color(image.getRGB(x, y)) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		out | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private def updateImage(in: Array[Array[Color]], out: BufferedImage): Unit = { | ||||||
|  | 		for (x <- in.indices; y <- in(0).indices) { | ||||||
|  | 			out.setRGB(x, y, in(x)(y).getRGB) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Applies the various filters to the buffer | ||||||
|  | 	 * | ||||||
|  | 	 * @param image The input image | ||||||
|  | 	 * @return The filtered image | ||||||
|  | 	 */ | ||||||
|  | 	override def transform(image: BufferedImage): BufferedImage = { | ||||||
|  | 		val img: Array[Array[Color]] = toArray(image) | ||||||
|  |  | ||||||
|  | 		// TODO Complete here by changing the assignment with your filters | ||||||
|  | 		val filtered: Array[Array[Color]] = img | ||||||
|  |  | ||||||
|  | 		updateImage(filtered, image) | ||||||
|  | 		return image | ||||||
|  | 	} | ||||||
|  | } | ||||||