Convert orx-shader-phrases and orx-noise to MPP

This commit is contained in:
Edwin Jakobs
2021-06-24 13:31:27 +02:00
parent 6a45db4491
commit 1cf5c825d6
69 changed files with 813 additions and 741 deletions

View File

@@ -0,0 +1,45 @@
# orx-boofcv
Helper functions to ease working with the BoofCV computer vision library
and its data types.
BoofCV is an open source library written from scratch for real-time
computer vision. Its functionality covers a range of subjects,
low-level image processing, camera calibration, feature detection/tracking,
structure-from-motion, fiducial detection, and recognition.
BoofCV has been released under an Apache 2.0 license for both
academic and commercial use.
Examples of what BoofCV offers can be found at
[http://boofcv.org/](http://boofcv.org/)
As BoofCV implements it's own data types for images, lines, points, etc.
this addon provides some helper functions to convert them to OPENRNDR types:
- Bindings: converts to and from `ColorBuffer`.
- Drawing: allows directly drawing BoofCV line segments and other shapes.
- Point conversion to and from `Vector2`.
- Contour conversion from `BoofCV.Contour` to `Shape` and `ShapeContour`.
- `ImageFlow` to `ColorBuffer` conversion.
<!-- __demos__ -->
## Demos
### DemoContours01
[source code](src/demo/kotlin/DemoContours01.kt)
![DemoContours01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-boofcv/images/DemoContours01Kt.png)
### DemoResize01
[source code](src/demo/kotlin/DemoResize01.kt)
![DemoResize01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-boofcv/images/DemoResize01Kt.png)
### DemoResize02
[source code](src/demo/kotlin/DemoResize02.kt)
![DemoResize02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-boofcv/images/DemoResize02Kt.png)
### DemoSimplified01
[source code](src/demo/kotlin/DemoSimplified01.kt)
![DemoSimplified01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-boofcv/images/DemoSimplified01Kt.png)

View File

@@ -0,0 +1,20 @@
sourceSets {
demo {
java {
srcDirs = ["src/demo/kotlin"]
compileClasspath += main.getCompileClasspath()
runtimeClasspath += main.getRuntimeClasspath()
}
}
}
def boofcvVersion = "0.37"
dependencies {
api("org.boofcv:boofcv-core:$boofcvVersion")
demoImplementation("org.openrndr:openrndr-application:$openrndrVersion")
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")
demoRuntimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion")
demoImplementation(sourceSets.getByName("main").output)
}

View File

@@ -0,0 +1,74 @@
import boofcv.alg.filter.binary.BinaryImageOps
import boofcv.alg.filter.binary.GThresholdImageOps
import boofcv.alg.filter.binary.ThresholdImageOps
import boofcv.struct.ConnectRule
import boofcv.struct.image.GrayU8
import org.openrndr.application
import org.openrndr.boofcv.binding.toGrayF32
import org.openrndr.boofcv.binding.toShapeContours
import org.openrndr.color.ColorRGBa
import org.openrndr.color.rgb
import org.openrndr.draw.loadImage
import org.openrndr.extensions.SingleScreenshot
import kotlin.math.cos
import kotlin.math.sin
suspend fun main() {
application {
program {
// -- this block is for automation purposes only
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
// Load an image, convert to BoofCV format using orx-boofcv
val input = loadImage("demo-data/images/image-001.png").toGrayF32()
// BoofCV: calculate a good threshold for the loaded image
val threshold = GThresholdImageOps.computeOtsu(input, 0.0, 255.0)
// BoofCV: use the threshold to convert the image to black and white
val binary = GrayU8(input.width, input.height)
ThresholdImageOps.threshold(input, binary, threshold.toFloat(), false)
// BoofCV: Contract and expand the white areas to remove noise
var filtered = BinaryImageOps.erode8(binary, 1, null)
filtered = BinaryImageOps.dilate8(filtered, 1, null)
// BoofCV: Calculate contours as vector data
val contours = BinaryImageOps.contour(filtered, ConnectRule.EIGHT, null)
// orx-boofcv: convert vector data to OPENRNDR ShapeContours
val externalShapes = contours.toShapeContours(true,
internal = false, external = true)
val internalShapes = contours.toShapeContours(true,
internal = true, external = false)
extend {
drawer.run {
// Zoom in and out over time
translate(bounds.center)
scale(1.5 + 0.5 * cos(seconds * 0.2))
translate(-bounds.center)
stroke = null
// Draw all external shapes
fill = rgb(0.2)
contours(externalShapes)
// Draw internal shapes one by one to set unique colors
internalShapes.forEachIndexed { i, shp ->
val shade = 0.2 + (i % 7) * 0.1 +
0.1 * sin(i + seconds)
fill = ColorRGBa.PINK.shade(shade)
contour(shp)
}
}
}
}
}
}

View File

@@ -0,0 +1,33 @@
import org.openrndr.application
import org.openrndr.boofcv.binding.*
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.loadImage
import org.openrndr.extensions.SingleScreenshot
suspend fun main() {
application {
program {
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
// Load an image, convert to BoofCV format using orx-boofcv
val input = loadImage("demo-data/images/image-001.png")
val scaled = input.resizeBy(0.5)
val scaled2 = input.resizeBy(0.25, convertToGray = true)
val scaled3 = input.resizeBy(0.1)
extend {
drawer.clear(ColorRGBa.BLACK)
drawer.translate(0.0, (height - scaled.bounds.height) / 2.0)
drawer.image(scaled)
drawer.image(scaled2, scaled.bounds.width, scaled.bounds.height - scaled2.height)
drawer.image(scaled3, scaled.bounds.width + scaled2.bounds.width, scaled.bounds.height - scaled3.height)
}
}
}
}

View File

@@ -0,0 +1,32 @@
import org.openrndr.application
import org.openrndr.boofcv.binding.*
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.loadImage
import org.openrndr.extensions.SingleScreenshot
suspend fun main() {
application {
program {
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
// Load an image, convert to BoofCV format using orx-boofcv
val input = loadImage("demo-data/images/image-001.png")
val scaled = input.resizeTo(input.width / 3)
val scaled2 = input.resizeTo(newHeight = input.height / 4, convertToGray = true)
val scaled3 = input.resizeTo(input.width / 5, input.height / 5)
extend {
drawer.clear(ColorRGBa.BLACK)
drawer.translate(0.0, (height - scaled.bounds.height) / 2.0)
drawer.image(scaled)
drawer.image(scaled2, scaled.bounds.width, scaled.bounds.height - scaled2.height)
drawer.image(scaled3, scaled.bounds.width + scaled2.bounds.width, scaled.bounds.height - scaled3.height)
}
}
}
}

View File

@@ -0,0 +1,109 @@
import boofcv.alg.filter.binary.BinaryImageOps
import boofcv.alg.filter.binary.GThresholdImageOps
import boofcv.alg.filter.binary.ThresholdImageOps
import boofcv.struct.ConnectRule
import boofcv.struct.image.GrayU8
import org.openrndr.application
import org.openrndr.boofcv.binding.toGrayF32
import org.openrndr.boofcv.binding.toShapeContours
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorBuffer
import org.openrndr.draw.isolatedWithTarget
import org.openrndr.draw.renderTarget
import org.openrndr.extensions.SingleScreenshot
import org.openrndr.math.CatmullRomChain2
import org.openrndr.math.Vector2
import org.openrndr.shape.Rectangle
import org.openrndr.shape.ShapeContour
import org.openrndr.shape.simplify
import org.openrndr.shape.toContour
suspend fun main() {
application {
program {
// -- this block is for automation purposes only
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
// Create a buffer where to draw something for boofcv
val rt = renderTarget(width, height) {
colorBuffer()
depthBuffer()
}
// Draw some shapes on that buffer
drawer.isolatedWithTarget(rt) {
clear(ColorRGBa.BLACK)
fill = ColorRGBa.WHITE
stroke = null
rectangle(Rectangle.fromCenter(bounds.position(0.33, 0.5),
150.0, 150.0))
translate(bounds.position(0.62, 0.5))
rotate(30.0)
rectangle(Rectangle.fromCenter(Vector2.ZERO, 200.0, 200.0))
rectangle(0.0, -200.0, 60.0, 60.0)
circle(0.0, 190.0, 60.0)
}
// Convert the bitmap buffer into ShapeContours
val vectorized = imageToContours(rt.colorBuffer(0))
// Show amount of segments in each shape (high number)
vectorized.forEachIndexed { i, it ->
println("boofcv shape $i: ${it.segments.size} segments")
}
// Make a simplified list of points
val simplePoints = vectorized.map {
simplify(it.adaptivePositions(), 4.0)
}.filter { it.size >= 3 }
// Use the simplified list to make a smooth contour
val smooth = simplePoints.map {
CatmullRomChain2(it, 0.0, true).toContour()
}
// Use the simplified list to make a polygonal contour
val polygonal = simplePoints.map {
ShapeContour.fromPoints(it, true)
}
// Show amount of segments in simplified shapes (low number).
// Note: `smooth` and `polygonal` have the same number of segments
smooth.forEachIndexed { i, it ->
println("simplified shape $i: ${it.segments.size} segments")
}
extend {
drawer.run {
fill = null // ColorRGBa.PINK.opacify(0.15)
stroke = ColorRGBa.PINK.opacify(0.7)
contours(polygonal)
contours(smooth)
}
}
}
}
}
fun imageToContours(input: ColorBuffer): List<ShapeContour> {
val bitmap = input.toGrayF32()
// BoofCV: calculate a good threshold for the loaded image
val threshold = GThresholdImageOps.computeOtsu(bitmap, 0.0, 255.0)
// BoofCV: use the threshold to convert the image to black and white
val binary = GrayU8(bitmap.width, bitmap.height)
ThresholdImageOps.threshold(bitmap, binary, threshold.toFloat(), false)
// BoofCV: Contract and expand the white areas to remove noise
var filtered = BinaryImageOps.erode8(binary, 1, null)
filtered = BinaryImageOps.dilate8(filtered, 1, null)
// BoofCV: Calculate contours as vector data
val contours = BinaryImageOps.contour(filtered, ConnectRule.EIGHT, null)
// orx-boofcv: convert vector data to OPENRNDR ShapeContours
return contours.toShapeContours(true, internal = true, external = true)
}

View File

@@ -0,0 +1,187 @@
package org.openrndr.boofcv.binding
import boofcv.struct.image.GrayF32
import boofcv.struct.image.GrayF64
import boofcv.struct.image.GrayU8
import boofcv.struct.image.Planar
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorBuffer
import org.openrndr.draw.ColorFormat
import org.openrndr.draw.ColorType
import org.openrndr.draw.colorBuffer
import kotlin.experimental.and
fun ColorBuffer.toGrayF32() : GrayF32 {
val p = GrayF32(width, height)
shadow.download()
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val c = shadow.read(x, y)
p.data[offset] = (c.r * 255).toFloat()
offset++
}
}
return p
}
fun ColorBuffer.toGrayF64() : GrayF64 {
val p = GrayF64(width, height)
shadow.download()
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val c = shadow.read(x, y)
p.data[offset] = (c.r * 255)
offset++
}
}
return p
}
fun ColorBuffer.toPlanarF32() : Planar<GrayF32> {
val p = Planar<GrayF32>(GrayF32::class.java, width, height, format.componentCount)
shadow.download()
val bands = p.bands
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val c = shadow.read(x, y)
bands[0].data[offset] = (c.r * 255).toFloat()
bands[1].data[offset] = (c.g * 255).toFloat()
bands[2].data[offset] = (c.b * 255).toFloat()
offset++
}
}
return p
}
fun ColorBuffer.toPlanarU8() : Planar<GrayU8> {
val p = Planar<GrayU8>(GrayU8::class.java, width, height, format.componentCount)
shadow.download()
val bands = p.bands
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val c = shadow.read(x, y)
bands[0].data[offset] = (c.r * 255).toInt().toByte()
bands[1].data[offset] = (c.g * 255).toInt().toByte()
bands[2].data[offset] = (c.b * 255).toInt().toByte()
offset++
}
}
return p
}
fun ColorBuffer.toGrayU8() : GrayU8 {
val p = GrayU8(width, height)
shadow.download()
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val c = shadow.read(x, y)
p.data[offset] = (c.r * 255).toInt().coerceIn(0, 255).toByte()
offset++
}
}
return p
}
fun GrayU8.toColorBuffer() : ColorBuffer {
val cb = colorBuffer(width, height, 1.0, ColorFormat.RGB, ColorType.UINT8)
val shadow = cb.shadow
shadow.buffer.rewind()
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val r = (data[offset].toInt() and 0xff).toDouble() / 255.0
offset++
shadow.write(x, y, ColorRGBa(r, r, r, 1.0))
}
}
shadow.upload()
return cb
}
fun GrayF32.toColorBuffer() : ColorBuffer {
val cb = colorBuffer(width, height, 1.0, ColorFormat.RGB, ColorType.FLOAT32)
val shadow = cb.shadow
shadow.buffer.rewind()
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val r = data[offset].toDouble() / 255.0
offset++
shadow.write(x, y, ColorRGBa(r, r, r))
}
}
shadow.upload()
return cb
}
fun Planar<GrayU8>.toColorBuffer() : ColorBuffer {
val bandCount = bands.size
val format = when (bandCount) {
1 -> ColorFormat.R
2 -> ColorFormat.RG
3 -> ColorFormat.RGB
4 -> ColorFormat.RGBa
else -> throw IllegalArgumentException("only 1 to 4 bands supported")
}
val bands = bands
val cb = colorBuffer(width, height, 1.0, format, ColorType.UINT8)
val shadow = cb.shadow
shadow.buffer.rewind()
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val r = (bands[0].data[offset].toInt() and 0xff).toDouble() / 255.0
val g = if (bandCount >= 2) (bands[1].data[offset].toInt() and 0xff).toDouble() / 255.0 else 0.0
val b = if (bandCount >= 3) (bands[2].data[offset].toInt() and 0xff).toDouble() / 255.0 else 0.0
val a = if (bandCount >= 4) (bands[2].data[offset].toInt() and 0xff).toDouble() / 255.0 else 1.0
offset++
shadow.write(x, y, ColorRGBa(r, g, b, a))
}
}
shadow.upload()
return cb
}
@JvmName("grayF32ToColorBuffer")
fun Planar<GrayF32>.toColorBuffer() : ColorBuffer {
val bandCount = bands.size
val format = when (bandCount) {
1 -> ColorFormat.R
2 -> ColorFormat.RG
3 -> ColorFormat.RGB
4 -> ColorFormat.RGBa
else -> throw IllegalArgumentException("only 1 to 4 bands supported")
}
val bands = bands
val cb = colorBuffer(width, height, 1.0, format, ColorType.UINT8)
val shadow = cb.shadow
shadow.buffer.rewind()
var offset = 0
for (y in 0 until height) {
for (x in 0 until width) {
val r = bands[0].data[offset] / 255.0
val g = if (bandCount >= 2) bands[1].data[offset] / 255.0 else 0.0
val b = if (bandCount >= 3) bands[2].data[offset] / 255.0 else 0.0
val a = if (bandCount >= 4) bands[3].data[offset] / 255.0 else 1.0
offset++
shadow.write(x, y, ColorRGBa(r, g, b, a))
}
}
shadow.upload()
return cb
}

View File

@@ -0,0 +1,51 @@
package org.openrndr.boofcv.binding
import boofcv.alg.filter.binary.Contour
import org.openrndr.shape.Shape
import org.openrndr.shape.ShapeContour
fun Contour.toShape(closed: Boolean = false, getInternal: Boolean, getExternal: Boolean): Shape {
val contours = mutableListOf<ShapeContour>()
if (getExternal) {
val externalPoints = external.toVector2s()
contours.addAll(listOf(ShapeContour.fromPoints(externalPoints, closed)))
}
if (getInternal) {
val internalCurves = internal.filter { it.size >= 3 }.map { it.toVector2s() }
contours.addAll(internalCurves.map { internalCurve ->
ShapeContour.fromPoints(internalCurve, closed)
})
}
return Shape(contours)
}
fun List<Contour>.toShapes(closed: Boolean = false,
internal: Boolean = true,
external: Boolean = true): List<Shape> {
return this.filter { it.external.size >= 3 }.map {
it.toShape(closed, internal, external)
}
}
fun List<Contour>.toShapeContours(closed: Boolean = false,
internal: Boolean = true,
external: Boolean = true): List<ShapeContour> {
val contours = mutableListOf<ShapeContour>()
this.forEach { contour ->
if(contour.external.size >= 3) {
if (external) {
val externalPoints = contour.external.toVector2s()
contours.add(ShapeContour.fromPoints(externalPoints, closed))
}
if (internal) {
val internalCurves = contour.internal.filter { it.size >= 3 }
.map { it.toVector2s() }
internalCurves.forEach { internalContour ->
contours.add(ShapeContour.fromPoints(internalContour, closed))
}
}
}
}
return contours
}

View File

@@ -0,0 +1,70 @@
package org.openrndr.boofcv.binding
import boofcv.abst.distort.FDistort
import boofcv.struct.image.ImageBase
import org.openrndr.draw.ColorBuffer
import org.openrndr.draw.ColorType
import kotlin.math.roundToInt
fun <T : ImageBase<out ImageBase<*>>?> ImageBase<T>.resizeBy(scaleX: Double, scaleY: Double = scaleX): T {
val scaled = this.createNew((this.width * scaleX).toInt(), (this.height * scaleY).toInt())
FDistort(this, scaled).scaleExt().apply()
return scaled
}
fun <T : ImageBase<out ImageBase<*>>?> ImageBase<T>.resizeTo(newWidth: Int? = null, newHeight: Int? = null): T {
val ar = this.width / this.height.toDouble()
val scaled = (if (newWidth != null && newHeight != null) {
val w = newWidth
val h = newHeight
this.createNew(w, h)
} else if (newWidth != null && newHeight == null) {
val w = newWidth
val h = newWidth / ar
this.createNew(w, h.roundToInt())
} else if (newWidth == null && newHeight != null) {
val w = newHeight * ar
val h = newHeight
this.createNew(w.roundToInt(), h)
} else {
this.createNew(this.width, this.height)
})
FDistort(this, scaled).scaleExt().apply()
return scaled
}
fun ColorBuffer.resizeBy(scaleX: Double, scaleY: Double = scaleX, convertToGray: Boolean = false): ColorBuffer {
return if (convertToGray) {
when (this.type) {
ColorType.FLOAT32, ColorType.FLOAT16 -> this.toGrayF32().resizeBy(scaleX, scaleY).toColorBuffer()
else -> this.toGrayU8().resizeBy(scaleX, scaleY).toColorBuffer()
}
} else {
when (this.type) {
ColorType.FLOAT32, ColorType.FLOAT16 -> this.toPlanarF32().resizeBy(scaleX, scaleY).toColorBuffer()
else -> this.toPlanarU8().resizeBy(scaleX, scaleY).toColorBuffer()
}
}
}
fun ColorBuffer.resizeTo(newWidth: Int? = null, newHeight: Int? = null, convertToGray: Boolean = false): ColorBuffer {
return if (convertToGray) {
when (this.type) {
ColorType.FLOAT32, ColorType.FLOAT16 -> this.toGrayF32().resizeTo(newWidth, newHeight).toColorBuffer()
else -> this.toGrayU8().resizeTo(newWidth, newHeight).toColorBuffer()
}
} else {
when (this.type) {
ColorType.FLOAT32, ColorType.FLOAT16 -> this.toPlanarF32().resizeTo(newWidth, newHeight).toColorBuffer()
else -> this.toPlanarU8().resizeTo(newWidth, newHeight).toColorBuffer()
}
}
}

View File

@@ -0,0 +1,83 @@
package org.openrndr.boofcv.binding
import georegression.struct.line.LineSegment2D_F32
import georegression.struct.line.LineSegment2D_F64
import georegression.struct.trig.Circle2D_F32
import georegression.struct.trig.Circle2D_F64
import org.openrndr.draw.Drawer
import org.openrndr.math.Vector2
import org.openrndr.shape.Circle
fun Drawer.lineSegment(segment: LineSegment2D_F32) {
lineSegment(
segment.a.x.toDouble(),
segment.a.y.toDouble(),
segment.b.x.toDouble(),
segment.b.y.toDouble()
)
}
@JvmName("lineSegments2D_F32")
fun Drawer.lineSegments(segments: List<LineSegment2D_F32>) {
lineSegments(
segments.flatMap { segment ->
listOf(
Vector2(segment.a.x.toDouble(), segment.a.y.toDouble()),
Vector2(segment.b.x.toDouble(), segment.b.y.toDouble())
)
}
)
}
fun Drawer.lineSegment(segment: LineSegment2D_F64) {
lineSegment(
segment.a.x,
segment.a.y,
segment.b.x,
segment.b.y
)
}
@JvmName("lineSegments2D_F64")
fun Drawer.lineSegments(segments: List<LineSegment2D_F64>) {
lineSegments(
segments.flatMap { segment ->
listOf(
Vector2(segment.a.x, segment.a.y),
Vector2(segment.b.x, segment.b.y)
)
}
)
}
fun Drawer.circle(circle: Circle2D_F32) {
circle(
circle.center.x.toDouble(), circle.center.y.toDouble(),
circle.radius.toDouble()
)
}
fun Drawer.circle(circle: Circle2D_F64) {
circle(
circle.center.x, circle.center.y,
circle.radius
)
}
@JvmName("circles2D_F32")
fun Drawer.circles(circles: List<Circle2D_F32>) {
circles(
circles.map {
Circle(it.center.x.toDouble(), it.center.y.toDouble(), it.radius.toDouble())
}
)
}
@JvmName("circles2D_F64")
fun Drawer.circles(circles: List<Circle2D_F64>) {
circles(
circles.map {
Circle(it.center.x.toDouble(), it.center.y.toDouble(), it.radius.toDouble())
}
)
}

View File

@@ -0,0 +1,33 @@
package org.openrndr.boofcv.binding
import boofcv.struct.flow.ImageFlow
import org.openrndr.draw.ColorBuffer
import org.openrndr.draw.ColorFormat
import org.openrndr.draw.ColorType
import org.openrndr.draw.colorBuffer
import java.nio.Buffer
import java.nio.ByteBuffer
import java.nio.ByteOrder
fun ImageFlow.toColorBuffer(): ColorBuffer {
val cb = colorBuffer(
width, height, format = ColorFormat.RG,
type = ColorType.FLOAT32
)
val bb = ByteBuffer.allocateDirect(width * height * 8)
bb.order(ByteOrder.nativeOrder())
for (y in 0 until height) {
for (x in 0 until width) {
val f = get(x, y)
bb.putFloat(f.x)
bb.putFloat(f.y)
}
}
(bb as Buffer).rewind()
cb.write(bb)
cb.flipV = true
return cb
}

View File

@@ -0,0 +1,19 @@
package org.openrndr.boofcv.binding
import georegression.struct.affine.Affine2D_F32
import georegression.struct.affine.Affine2D_F64
import org.openrndr.math.Matrix44
fun Affine2D_F32.toMatrix44() = Matrix44(
c0r0 = a11.toDouble(), c1r0 = a12.toDouble(), c3r0 = tx.toDouble(),
c0r1 = a21.toDouble(), c1r1 = a22.toDouble(), c3r1 = ty.toDouble(),
c2r2 = 1.0,
c3r3 = 1.0
)
fun Affine2D_F64.toMatrix44() = Matrix44(
c0r0 = a11, c1r0 = a12, c3r0 = tx,
c0r1 = a21, c1r1 = a22, c3r1 = ty,
c2r2 = 1.0,
c3r3 = 1.0
)

View File

@@ -0,0 +1,11 @@
package org.openrndr.boofcv.binding
import georegression.struct.point.Point2D_F32
import georegression.struct.point.Point2D_F64
import georegression.struct.point.Point2D_I32
import org.openrndr.math.Vector2
fun Point2D_I32.toVector2() = Vector2(x.toDouble(), y.toDouble())
fun Point2D_F32.toVector2() = Vector2(x.toDouble(), y.toDouble())
fun Point2D_F64.toVector2() = Vector2(x.toDouble(), y.toDouble())
fun List<Point2D_I32>.toVector2s() = this.map { it.toVector2() }