Skip to main content

Seam Carving (Presentation & Workshop)

· 5 min read
Hampus Londögård

This is from a presentation I did last week (12th of May 2021). Notebook just under the slides!
Please note that this requires the Kotlin kernel to run as it's Kotlin and not Python.

@file:DependsOn("org.boofcv:boofcv-core:0.37")
@file:DependsOn("org.boofcv:boofcv-kotlin:0.37")
%use lib-ext
import boofcv.abst.filter.derivative.ImageGradient_SB
import boofcv.kotlin.*
import boofcv.alg.feature.detect.edge.GGradientToEdgeFeatures
import boofcv.factory.feature.detect.edge.FactoryEdgeDetectors
import boofcv.io.image.ConvertBufferedImage
import boofcv.struct.image.*
import boofcv.io.image.UtilImageIO
import java.io.*
import javax.imageio.ImageIO
import java.nio.file.*
import java.awt.image.BufferedImage
val sobel: ImageGradient_SB.Sobel<GrayF32, GrayF32> by lazy {
ImageGradient_SB.Sobel(GrayF32::class.java, GrayF32::class.java)
}

fun BufferedImage.toByteArray(format: String): ByteArray {
val stream = ByteArrayOutputStream()
ImageIO.write(this, format, stream)
return stream.toByteArray()
}
fun FloatArray.removeIndices(toRemove: Set<Int>): FloatArray {
val result = FloatArray(size - toRemove.size)
var targetIndex = 0
for (sourceIndex in indices) {
if (sourceIndex !in toRemove) result[targetIndex++] = this[sourceIndex]
}
return result
}
fun ByteArray.removeIndices(toRemove: Set<Int>): ByteArray {
val result = ByteArray(size - toRemove.size)
var targetIndex = 0
for (sourceIndex in indices) {
if (sourceIndex !in toRemove) result[targetIndex++] = this[sourceIndex]
}
return result
}

Loading an Image with BoofCV​

Let's load our first image using BoofCV. It's simply done using UtilImageIO.loadImage("path/to/file").
The conversion to show an image in a notebook is a little awkward.

But I talked to Jetbrains through Slack - had a response & patch up within 20 minutes (waiting for the new release right now...) which simplifies this!

val img = UtilImageIO.loadImage("dali.jpg")
Image(img.toByteArray("jpg"), "jpg").withWidth("45%")

Sobel Filters​

Sobel is a very simple Edge Detector that runs the gradient in two directions
sobel

image image (18.S191 MIT Fall 2020 | Grant Sanderson) By applying this filter

val grayImg = img.asGrayF32()
Image(grayImg.asBufferedImage().toByteArray("jpg"), "jpg").withWidth("45%")
val grayDY = GrayF32(1,1)
val grayDX = GrayF32(1,1)
sobel.process(grayImg, grayDX, grayDY)
DISPLAY(Image(grayDY.asBufferedImage().toByteArray("jpg"), "jpg").withWidth(500))
Image(grayDX.asBufferedImage().toByteArray("jpg"), "jpg").withWidth(500)