Compare commits
	
		
			17 Commits
		
	
	
		
			543eb21595
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 683fbd912f | |||
| 6df1a451ec | |||
| 791f6b7b88 | |||
| 979807f399 | |||
| 0657c28d7d | |||
| ee6999b095 | |||
| f8db769ec3 | |||
| cdd9a08635 | |||
| 0943bfa193 | |||
| 0a7a647126 | |||
| ddd8952e9a | |||
| 54fa312113 | |||
| ddddbfe558 | |||
| 9454948799 | |||
| 16e6e51472 | |||
| e33f77b46e | |||
| 309088e5d2 | 
							
								
								
									
										18
									
								
								Lab11.iml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,18 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="WEB_MODULE" version="4"> | ||||
|   <component name="NewModuleRootManager" inherit-compiler-output="true"> | ||||
|     <exclude-output /> | ||||
|     <content url="file://$MODULE_DIR$"> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/res" type="java-resource" /> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> | ||||
|     </content> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|     <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" /> | ||||
|     <orderEntry type="library" name="bytedeco.javacv" level="project" /> | ||||
|     <orderEntry type="library" name="bytedeco.javacv.platform" level="project" /> | ||||
|   </component> | ||||
| </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 | 
							
								
								
									
										9
									
								
								src/TestArray.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | ||||
| object TestArray extends App { | ||||
|   def arrayAvg(arr: Array[Double]): Double = { | ||||
|     if (arr.isEmpty) return 0 | ||||
|     return arr.sum / arr.length | ||||
|   } | ||||
|  | ||||
|   val arr: Array[Double] = Array.range(1, 1001).map(i => i.toDouble) | ||||
|   println(arrayAvg(arr)) | ||||
| } | ||||
							
								
								
									
										188
									
								
								src/imagefilters/ImageFilters.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,188 @@ | ||||
| package imagefilters | ||||
|  | ||||
| import java.awt.Color | ||||
|  | ||||
| /** | ||||
|  * This class implements the various image filters | ||||
|  */ | ||||
| object ImageFilters { | ||||
|   // Default factors (reference values) | ||||
|   /*val SEPIA_FACTORS: Array[Array[Double]] = Array( | ||||
|     Array( 0.393, 0.769, 0.189), | ||||
|     Array( 0.349, 0.686, 0.168), | ||||
|     Array( 0.272, 0.534, 0.131) | ||||
|   )*/ | ||||
|  | ||||
|   // Adapted factors (matches the example in the instructions) | ||||
|   val SEPIA_FACTORS: Array[Array[Double]] = Array( | ||||
|     Array( 0.883, 0.004, 0.003), | ||||
|     Array(-0.058, 0.791,-0.097), | ||||
|     Array( 0.034,-0.244, 0.583) | ||||
|   ) | ||||
|  | ||||
|   def filter(src: Array[Array[Int]], func: (Int, Int, Int, Int, Int) => Int): Array[Array[Int]] = { | ||||
|     val width: Int = src.length | ||||
|     val height: Int = if (width == 0) 0 else src(0).length | ||||
|     val dst: Array[Array[Int]] = Array.ofDim(width, height) | ||||
|     for (x: Int <- 0 until width) { | ||||
|       for (y: Int <- 0 until height) { | ||||
|         dst(x)(y) = func(src(x)(y), x, y, width, height) | ||||
|       } | ||||
|     } | ||||
|     return dst | ||||
|   } | ||||
|   def filter(src: Array[Array[Int]], func: (Int) => Int): Array[Array[Int]] = filter(src, (value, x, y, width, height) => func(value)) | ||||
|   def colorFilter(src: Array[Array[Color]], func: (Color, Int, Int, Int, Int) => Color): Array[Array[Color]] = { | ||||
|     val width: Int = src.length | ||||
|     val height: Int = if (width == 0) 0 else src(0).length | ||||
|     val dst: Array[Array[Color]] = Array.ofDim(width, height) | ||||
|     for (x: Int <- 0 until width) { | ||||
|       for (y: Int <- 0 until height) { | ||||
|         dst(x)(y) = func(src(x)(y), x, y, width, height) | ||||
|       } | ||||
|     } | ||||
|     return dst | ||||
|   } | ||||
|   def colorFilter(src: Array[Array[Color]], func: (Color) => Color): Array[Array[Color]] = colorFilter(src, (value, x, y, width, height) => func(value)) | ||||
|  | ||||
|   def duplicate(a: Array[Array[Int]]): Array[Array[Int]] = filter(a, (value) => value) | ||||
|   def threshold(a: Array[Array[Int]], thresh: Int): Array[Array[Int]] = filter(a, (value) => if (value > thresh) 255 else 0) | ||||
|   def mean(a: Array[Array[Int]]): Array[Array[Int]] = filter(a, (value, x, y, width, height) => { | ||||
|     if (x == 0 || x == width - 1 || y == 0 || y == height - 1) value | ||||
|     else { | ||||
|       var avg: Double = 0 | ||||
|       for (dx: Int <- -1 to 1) { | ||||
|         for (dy: Int <- -1 to 1) { | ||||
|           avg += a(x+dx)(y+dy) | ||||
|         } | ||||
|       } | ||||
|       (avg/9.0).toInt | ||||
|     } | ||||
|   }) | ||||
|   def mean(a: Array[Array[Int]], radius: Int): Array[Array[Int]] = { | ||||
|     if (radius < 0) throw new IllegalArgumentException("radius must be >= 0") | ||||
|     val diameter: Int = 2 * radius + 1 | ||||
|  | ||||
|     filter(a, (value, x, y, width, height) => { | ||||
|       if (x <= radius - 1 || x >= width - radius || y <= radius - 1 || y >= height - radius) value | ||||
|       else { | ||||
|         var avg: Double = 0 | ||||
|         for (dx: Int <- -radius to radius) { | ||||
|           for (dy: Int <- -radius to radius) { | ||||
|             avg += a(x+dx)(y+dy) | ||||
|           } | ||||
|         } | ||||
|         (avg/(diameter * diameter)).toInt | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   def edges(a: Array[Array[Int]]): Array[Array[Int]] = filter(a, (value, x, y, width, height) => { | ||||
|     if (x == 0 || x == width - 1 || y == 0 || y == height - 1) value | ||||
|     else { | ||||
|       val dx: Int = a(x+1)(y) - a(x-1)(y) | ||||
|       val dy: Int = a(x)(y+1) - a(x)(y-1) | ||||
|       Math.sqrt(dx*dx + dy*dy).toInt | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   def sobel(a: Array[Array[Int]], intensityFactor: Double): Array[Array[Int]] = filter(a, (value, x, y, width, height) => { | ||||
|     if (x == 0 || x == width - 1 || y == 0 || y == height - 1) value | ||||
|     else { | ||||
|       val dx: Int = a(x+1)(y-1) + 2*a(x+1)(y) + a(x+1)(y+1) - a(x-1)(y-1) - 2*a(x-1)(y) - a(x-1)(y+1) | ||||
|       val dy: Int = a(x-1)(y+1) + 2*a(x)(y+1) + a(x+1)(y+1) - a(x-1)(y-1) - 2*a(x)(y-1) - a(x+1)(y-1) | ||||
|       (Math.sqrt(dx*dx + dy*dy)*intensityFactor).toInt | ||||
|     } | ||||
|   }) | ||||
|  | ||||
|   def noise(a: Array[Array[Int]], intensity: Double): Array[Array[Int]] = filter(a, (value) => { | ||||
|     Math.max(0, Math.min(255, value + (Math.random()*2-1)*intensity)).toInt | ||||
|   }) | ||||
|  | ||||
|   def noise(a: Array[Array[Color]], intensity: Double): Array[Array[Color]] = colorFilter(a, (col) => { | ||||
|     val r: Double = col.getRed + (Math.random()*2-1)*intensity | ||||
|     val g: Double = col.getGreen + (Math.random()*2-1)*intensity | ||||
|     val b: Double = col.getBlue + (Math.random()*2-1)*intensity | ||||
|     new Color( | ||||
|       Math.max(0, Math.min(255, r)).toInt, | ||||
|       Math.max(0, Math.min(255, g)).toInt, | ||||
|       Math.max(0, Math.min(255, b)).toInt | ||||
|     ) | ||||
|   }) | ||||
|  | ||||
|   def mask(a: Array[Array[Color]], maskImg: Array[Array[Int]]): Array[Array[Color]] = { | ||||
|     val maskWidth: Int = maskImg.length | ||||
|     val maskHeight: Int = if (maskWidth == 0) 0 else maskImg(0).length | ||||
|  | ||||
|     colorFilter(a, (col, x, y, width, height) => { | ||||
|       val maskX: Int = (x.toDouble / width * maskWidth).toInt | ||||
|       val maskY: Int = (y.toDouble / height * maskHeight).toInt | ||||
|       val factor: Double = maskImg(maskX)(maskY)/255.0 | ||||
|       new Color( | ||||
|         (col.getRed * factor).toInt, | ||||
|         (col.getGreen * factor).toInt, | ||||
|         (col.getBlue * factor).toInt | ||||
|       ) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   def getAvgInPixel(a: Array[Array[Color]], pixelSize: Int, px: Int, py: Int): Color = { | ||||
|     val width: Int = a.length | ||||
|     val height: Int = if (width == 0) 0 else a(0).length | ||||
|     var r: Double = 0 | ||||
|     var g: Double = 0 | ||||
|     var b: Double = 0 | ||||
|     var cnt: Int = 0 | ||||
|     for (x: Int <- px*pixelSize until (px+1)*pixelSize) { | ||||
|       for (y: Int <- py*pixelSize until (py+1)*pixelSize) { | ||||
|         if (x < width && y < height) { | ||||
|           cnt += 1 | ||||
|           r += a(x)(y).getRed | ||||
|           g += a(x)(y).getGreen | ||||
|           b += a(x)(y).getBlue | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return new Color((r/cnt).toInt, (g/cnt).toInt, (b/cnt).toInt) | ||||
|   } | ||||
|   def pixelize(a: Array[Array[Color]], pixelSize: Int): Array[Array[Color]] = { | ||||
|     val width: Int = a.length | ||||
|     val height: Int = if (width == 0) 0 else a(0).length | ||||
|     val hPixels: Int = Math.ceil(width.toDouble/pixelSize).toInt | ||||
|     val vPixels: Int = Math.ceil(height.toDouble/pixelSize).toInt | ||||
|     val pixels: Array[Array[Color]] = Array.ofDim[Color](hPixels, vPixels) | ||||
|     for (x: Int <- 0 until hPixels) { | ||||
|       for (y: Int <- 0 until vPixels) { | ||||
|         pixels(x)(y) = getAvgInPixel(a, pixelSize, x, y) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     colorFilter(a, (col, x, y, w, h) => pixels(x/pixelSize)(y/pixelSize)) | ||||
|   } | ||||
|  | ||||
|   def pixelize(a: Array[Array[Color]], pixelSize: Int, x1: Int, y1: Int, x2: Int, y2: Int): Array[Array[Color]] = { | ||||
|     val pixelated: Array[Array[Color]] = pixelize(a, pixelSize) | ||||
|     colorFilter(a, (col, x, y, w, h) => { | ||||
|       if (x1 <= x && x <= x2 && y1 <= y && y <= y2) { | ||||
|         pixelated(x)(y) | ||||
|       } | ||||
|       else col | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   def sepia(a: Array[Array[Color]]): Array[Array[Color]] = colorFilter(a, (col) => { | ||||
|     val r: Double = Math.max(0, Math.min(255, col.getRed   * SEPIA_FACTORS(0)(0) + | ||||
|                                               col.getGreen * SEPIA_FACTORS(0)(1) + | ||||
|                                               col.getBlue  * SEPIA_FACTORS(0)(2))) | ||||
|  | ||||
|     val g: Double = Math.max(0, Math.min(255, col.getRed   * SEPIA_FACTORS(1)(0) + | ||||
|                                               col.getGreen * SEPIA_FACTORS(1)(1) + | ||||
|                                               col.getBlue  * SEPIA_FACTORS(1)(2))) | ||||
|  | ||||
|     val b: Double = Math.max(0, Math.min(255, col.getRed   * SEPIA_FACTORS(2)(0) + | ||||
|                                               col.getGreen * SEPIA_FACTORS(2)(1) + | ||||
|                                               col.getBlue  * SEPIA_FACTORS(2)(2))) | ||||
|  | ||||
|     new Color(r.toInt, g.toInt, b.toInt) | ||||
|   }) | ||||
| } | ||||
							
								
								
									
										64
									
								
								src/imagefilters/ImageProcessingApp.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,64 @@ | ||||
| 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) | ||||
| 	val thresh = new ImageGraphics(imageFile, "Threshold", -500, 250) | ||||
|  | ||||
|     // Simple copy and display | ||||
| 	val newPixels = ImageFilters.duplicate(org.getPixelsBW()) | ||||
| 	dest.setPixelsBW(newPixels) | ||||
|  | ||||
| 	thresh.setPixelsBW(ImageFilters.threshold(newPixels, 128)) | ||||
|  | ||||
| 	val mean = new ImageGraphics("./res/imageProcessing.jpg", "Mean", 0, 250) | ||||
| 	mean.setPixelsBW(ImageFilters.mean(mean.getPixelsBW())) | ||||
|  | ||||
| 	val mean2 = new ImageGraphics("./res/imageProcessing.jpg", "Mean 2", 250, 250) | ||||
| 	mean2.setPixelsBW(ImageFilters.mean(mean2.getPixelsBW(), 4)) | ||||
|  | ||||
| 	val edges = new ImageGraphics("./res/rice.jpg", "Edges", 500, 250) | ||||
| 	edges.setPixelsBW(ImageFilters.edges(edges.getPixelsBW()))*/ | ||||
|  | ||||
| 	/*val imageFile: String = "./res/grace_hopper.jpg" | ||||
| 	val org = new ImageGraphics(imageFile, "Original", -768, -512) | ||||
| 	val bw = new ImageGraphics(imageFile, "Black & White", -256, -512) | ||||
| 	val threshold = new ImageGraphics(imageFile, "Threshold", 256, -512) | ||||
| 	val blur = new ImageGraphics(imageFile, "Blurred", -768, 0) | ||||
| 	val edges = new ImageGraphics(imageFile, "Edge detection", -256, 0) | ||||
| 	val sobel = new ImageGraphics(imageFile, "Sobel", 256, 0) | ||||
| 	val noise = new ImageGraphics(imageFile, "Noise", -768, 512) | ||||
|  | ||||
| 	bw.setPixelsBW(ImageFilters.duplicate(org.getPixelsBW())) | ||||
| 	threshold.setPixelsBW(ImageFilters.threshold(org.getPixelsBW(), 128)) | ||||
| 	blur.setPixelsBW(ImageFilters.mean(org.getPixelsBW(), 3)) | ||||
| 	edges.setPixelsBW(ImageFilters.edges(org.getPixelsBW())) | ||||
| 	sobel.setPixelsBW(ImageFilters.sobel(org.getPixelsBW(), 0.3)) | ||||
| 	noise.setPixelsBW(ImageFilters.noise(org.getPixelsBW(), 30))*/ | ||||
|  | ||||
| 	/*val imageFile: String = "./res/collins_eileen.png" | ||||
| 	val maskFile: String = "./res/mask.png" | ||||
| 	val org = new ImageGraphics(imageFile, "Original", -768, -512) | ||||
| 	val mask = new ImageGraphics(maskFile, "Mask", -256, -512) | ||||
| 	val masked = new ImageGraphics(imageFile, "Masked", 256, -512) | ||||
| 	masked.setPixelsColor(ImageFilters.mask(org.getPixelsColor(), mask.getPixelsBW()))*/ | ||||
|  | ||||
| 	/*val imageFile: String = "./res/grace_hopper.jpg" | ||||
| 	val org = new ImageGraphics(imageFile, "Original", -768, -512) | ||||
| 	val pixelated = new ImageGraphics(imageFile, "Pixelated", -256, -512) | ||||
| 	pixelated.setPixelsColor(ImageFilters.pixelize(org.getPixelsColor(), 30, 130, 120, 400, 230))*/ | ||||
|  | ||||
| 	val imageFile: String = "./res/collins_eileen.png" | ||||
| 	val org = new ImageGraphics(imageFile, "Original", -768, -512) | ||||
| 	val sepia = new ImageGraphics(imageFile, "Sepia", -256, -512) | ||||
| 	val noise = new ImageGraphics(imageFile, "Noise", 256, -512) | ||||
| 	val mask = new ImageGraphics("./res/mask.png", "Mask", -256, -256) | ||||
| 	val masked = new ImageGraphics(imageFile, "Masked", 256, -256) | ||||
| 	sepia.setPixelsColor(ImageFilters.sepia(org.getPixelsColor())) | ||||
| 	noise.setPixelsColor(ImageFilters.noise(org.getPixelsColor(), 50)) | ||||
| 	masked.setPixelsColor(ImageFilters.mask(org.getPixelsColor(), mask.getPixelsBW())) | ||||
| } | ||||
							
								
								
									
										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 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										36
									
								
								src/livefilter/FaceDetection.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,36 @@ | ||||
| package livefilter | ||||
|  | ||||
| import org.bytedeco.javacpp.Loader | ||||
| import org.bytedeco.javacpp.indexer.UByteRawIndexer | ||||
| import org.bytedeco.opencv.global.opencv_core.CV_8U | ||||
| import org.bytedeco.opencv.opencv_core.{Mat, Rect, RectVector} | ||||
| import org.bytedeco.opencv.opencv_objdetect.CascadeClassifier | ||||
|  | ||||
| import java.io.File | ||||
| import java.net.URL | ||||
|  | ||||
| object FaceDetection { | ||||
|   val url: URL = new URL("https://raw.github.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_alt.xml") | ||||
|   val file: File = Loader.cacheResource(url) | ||||
|   val classifierName: String = file.getAbsolutePath | ||||
|   val cascade: CascadeClassifier = new CascadeClassifier(classifierName) | ||||
|  | ||||
|   def detectFaces(src: Array[Array[Int]]): Array[Rect] = { | ||||
|     val grayFrame: Mat = new Mat() | ||||
|     val width: Int = src.length | ||||
|     val height: Int = if (width == 0) 0 else src(0).length | ||||
|     grayFrame.create(height, width, CV_8U) | ||||
|  | ||||
|     val idx: UByteRawIndexer = grayFrame.createIndexer() | ||||
|  | ||||
|     for (x: Int <- 0 until width) { | ||||
|       for (y: Int <- 0 until height) { | ||||
|         idx.put(y, x, src(x)(y)) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     val faces: RectVector = new RectVector() | ||||
|     cascade.detectMultiScale(grayFrame, faces) | ||||
|     return faces.get() | ||||
|   } | ||||
| } | ||||
							
								
								
									
										103
									
								
								src/livefilter/LiveFilter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,103 @@ | ||||
| package livefilter | ||||
|  | ||||
| import com.github.sarxos.webcam.WebcamPanel.DrawMode | ||||
| import com.github.sarxos.webcam.{Webcam, WebcamImageTransformer, WebcamPanel, WebcamResolution} | ||||
| import imagefilters.ImageFilters | ||||
| import org.bytedeco.opencv.opencv_core.{Rect, RectVector} | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 		val bw: Array[Array[Int]] = toBW(image) | ||||
| 		val faces: Array[Rect] = FaceDetection.detectFaces(bw) | ||||
| 		var filtered: Array[Array[Color]] = img | ||||
| 		for (rect: Rect <- faces) { | ||||
|       filtered = ImageFilters.pixelize(filtered, 10, rect.x, rect.y, rect.x+rect.width, rect.y+rect.height) | ||||
|     } | ||||
|  | ||||
| 		//val filtered: Array[Array[Color]] = ImageFilters.mask(ImageFilters.sepia(ImageFilters.noise(img, 10)), videoMask) | ||||
|  | ||||
| 		updateImage(filtered, image) | ||||
| 		return image | ||||
| 	} | ||||
| } | ||||