190 lines
7.1 KiB
Scala
190 lines
7.1 KiB
Scala
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)
|
|
})
|
|
}
|