diff --git a/Lab11.iml b/Lab11.iml index 4de040d..c703763 100644 --- a/Lab11.iml +++ b/Lab11.iml @@ -8,5 +8,8 @@ + + + \ No newline at end of file diff --git a/libs/bridj-0.7.0.jar b/libs/bridj-0.7.0.jar new file mode 100644 index 0000000..dd285c7 Binary files /dev/null and b/libs/bridj-0.7.0.jar differ diff --git a/libs/slf4j-api-1.7.2.jar b/libs/slf4j-api-1.7.2.jar new file mode 100644 index 0000000..1a88708 Binary files /dev/null and b/libs/slf4j-api-1.7.2.jar differ diff --git a/libs/webcam-capture-0.3.12.jar b/libs/webcam-capture-0.3.12.jar new file mode 100644 index 0000000..4442b2e Binary files /dev/null and b/libs/webcam-capture-0.3.12.jar differ diff --git a/res/Dead_tree.png b/res/Dead_tree.png new file mode 100644 index 0000000..08bf780 Binary files /dev/null and b/res/Dead_tree.png differ diff --git a/res/collins_eileen.png b/res/collins_eileen.png new file mode 100644 index 0000000..e8be92a Binary files /dev/null and b/res/collins_eileen.png differ diff --git a/res/grace_hopper.jpg b/res/grace_hopper.jpg new file mode 100644 index 0000000..478720d Binary files /dev/null and b/res/grace_hopper.jpg differ diff --git a/res/imageProcessing.jpg b/res/imageProcessing.jpg new file mode 100644 index 0000000..92ae49b Binary files /dev/null and b/res/imageProcessing.jpg differ diff --git a/res/imageProcessing_empty.jpg b/res/imageProcessing_empty.jpg new file mode 100644 index 0000000..c415c7e Binary files /dev/null and b/res/imageProcessing_empty.jpg differ diff --git a/res/mask.png b/res/mask.png new file mode 100644 index 0000000..b557939 Binary files /dev/null and b/res/mask.png differ diff --git a/res/mask640x480.png b/res/mask640x480.png new file mode 100644 index 0000000..ebe9452 Binary files /dev/null and b/res/mask640x480.png differ diff --git a/res/moire1.png b/res/moire1.png new file mode 100644 index 0000000..fa73e3d Binary files /dev/null and b/res/moire1.png differ diff --git a/res/moire2.jpg b/res/moire2.jpg new file mode 100644 index 0000000..b0d296a Binary files /dev/null and b/res/moire2.jpg differ diff --git a/res/rice.jpg b/res/rice.jpg new file mode 100644 index 0000000..c778fe1 Binary files /dev/null and b/res/rice.jpg differ diff --git a/src/imagefilters/ImageFilters.scala b/src/imagefilters/ImageFilters.scala new file mode 100644 index 0000000..a412260 --- /dev/null +++ b/src/imagefilters/ImageFilters.scala @@ -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 + } +} diff --git a/src/imagefilters/ImageProcessingApp.scala b/src/imagefilters/ImageProcessingApp.scala new file mode 100644 index 0000000..3fcbaa0 --- /dev/null +++ b/src/imagefilters/ImageProcessingApp.scala @@ -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) +} diff --git a/src/isc/graphics/ImageGraphics.scala b/src/isc/graphics/ImageGraphics.scala new file mode 100644 index 0000000..9d98552 --- /dev/null +++ b/src/isc/graphics/ImageGraphics.scala @@ -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 ImageProcessing lab. It expects the images to reside in the src 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 + } +} diff --git a/src/livefilter/LiveFilter.scala b/src/livefilter/LiveFilter.scala new file mode 100644 index 0000000..d1ba393 --- /dev/null +++ b/src/livefilter/LiveFilter.scala @@ -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 + } +} \ No newline at end of file