Compare commits
16 Commits
543eb21595
...
6df1a451ec
| Author | SHA1 | Date | |
|---|---|---|---|
| 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))
|
||||
}
|
||||
189
src/imagefilters/ImageFilters.scala
Normal file
@@ -0,0 +1,189 @@
|
||||
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) => {
|
||||
if (x >= maskWidth || y >= maskHeight) col
|
||||
else {
|
||||
val factor: Double = maskImg(x)(y)/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)
|
||||
})
|
||||
}
|
||||
61
src/imagefilters/ImageProcessingApp.scala
Normal file
@@ -0,0 +1,61 @@
|
||||
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)
|
||||
sepia.setPixelsColor(ImageFilters.sepia(org.getPixelsColor()))
|
||||
noise.setPixelsColor(ImageFilters.noise(org.getPixelsColor(), 50))
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||