Skip to content

Commit

Permalink
feat(plugin-image): 新增形态学,图片转01,图片处理功能
Browse files Browse the repository at this point in the history
  • Loading branch information
Leon406 committed Dec 23, 2022
1 parent 0703b3b commit 52b57e5
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 42 deletions.
2 changes: 1 addition & 1 deletion plugin-image/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group = "me.leon.toolsfx"
version = "1.1.0"
version = "1.2.0"

plugins {
`java-library`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
package me.leon.toolsfx.plugin

import java.awt.image.BufferedImage
import java.io.File
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleStringProperty
import javafx.geometry.Pos
import javafx.scene.control.RadioButton
import javafx.scene.control.TextArea
import javafx.scene.control.*
import javafx.scene.image.Image
import javafx.scene.image.ImageView
import me.leon.*
import me.leon.ext.DEFAULT_SPACING
import me.leon.ext.cast
import me.leon.ext.fx.clipboardText
import me.leon.ext.fx.copy
import me.leon.ext.fx.fileDraggedHandler
import me.leon.ext.properText
import me.leon.ext.toFile
import me.leon.toolsfx.plugin.ext.ImageServiceType
import me.leon.toolsfx.plugin.ext.locationServiceType
import me.leon.toolsfx.plugin.ext.serviceTypeMap
import me.leon.ext.*
import me.leon.ext.fx.*
import me.leon.toolsfx.plugin.ext.*
import tornadofx.*

class ImageProcessView : PluginFragment("ImageProcessView") {
override val version = "v1.1.0"
override val date: String = "2022-12-17"
override val version = "v1.2.0"
override val date: String = "2022-12-23"
override val author = "Leon406"
override val description = "图片模块"
private var taInput: TextArea by singleAssign()
private var ivInput: ImageView by singleAssign()
private var taOutput: TextArea by singleAssign()
private var img: ImageView by singleAssign()
private var ivOutput: ImageView by singleAssign()
private var param1: TextField by singleAssign()
private var cbParam: ComboBox<String> by singleAssign()

private val fileMode = SimpleBooleanProperty(true)
private val showInputImage = SimpleBooleanProperty(false)
private val showImage = SimpleBooleanProperty(false)
private val showParams = SimpleBooleanProperty(false)
private val showComboParam = SimpleBooleanProperty(false)
private val selectedParam = SimpleStringProperty("")

private val paramsMap: Map<String, String>
get() = mutableMapOf(P1 to param1.text, C1 to selectedParam.get())

private val eventHandler = fileDraggedHandler {
taInput.text =
Expand All @@ -57,16 +61,39 @@ class ImageProcessView : PluginFragment("ImageProcessView") {
spacing = DEFAULT_SPACING
label(messages["input"])
checkbox("文件模式", fileMode)
checkbox("显示图片", showInputImage) {
selectedProperty().addListener { _, _, newValue ->
if (newValue) {
val file = taInput.text.toFile()
if (file.exists() && EXTENSION_IMAGE.contains(file.realExtension())) {
ivInput.image = file.toImage()
} else {
ivInput.image = null
}
} else {
ivInput.image = null
}
}
}
button(graphic = imageview(IMG_IMPORT)) {
tooltip(messages["pasteFromClipboard"])
action { taInput.text = clipboardText() }
}
}

taInput = textarea {
isWrapText = true
onDragEntered = eventHandler
stackpane {
taInput = textarea {
isWrapText = true
onDragEntered = eventHandler
visibleWhen(!showInputImage)
}
ivInput = imageview()
scrollpane(true) {
visibleWhen(showInputImage)
content = ivInput
}
}

hbox {
alignment = Pos.CENTER_LEFT
paddingTop = DEFAULT_SPACING
Expand All @@ -77,7 +104,7 @@ class ImageProcessView : PluginFragment("ImageProcessView") {
hgap = 8.0
vgap = 8.0
alignment = Pos.TOP_LEFT
prefColumns = 4
prefColumns = 5
togglegroup {
serviceTypeMap.forEach {
radiobutton(it.key) {
Expand All @@ -87,17 +114,36 @@ class ImageProcessView : PluginFragment("ImageProcessView") {
}
selectedToggleProperty().addListener { _, _, new ->
imageServiceType = new.cast<RadioButton>().text.locationServiceType()
val paramHints = imageServiceType.paramsHints()
showParams.value = paramHints.isNotEmpty()
param1.promptText = "".takeIf { paramHints.isEmpty() } ?: paramHints.first()

val options = imageServiceType.options()
if (options.isNotEmpty()) {
showComboParam.value = options.isNotEmpty()
cbParam.items = options.toMutableList().asObservable()
selectedParam.set(if (options.isEmpty()) "" else options.first())
cbParam.bind(selectedParam)
}

println(imageServiceType)
println(
"params ${paramHints.contentToString()} options ${options.contentToString()}"
)
}
}
}
}

hbox {
addClass(Styles.group, Styles.left)
cbParam = combobox(selectedParam) { visibleWhen(showComboParam) }
param1 = textfield { visibleWhen(showParams) }
}
hbox {
alignment = Pos.CENTER_LEFT
spacing = DEFAULT_SPACING
paddingLeft = DEFAULT_SPACING

button(messages["run"], imageview(IMG_RUN)) { action { doProcess() } }
}
hbox {
Expand All @@ -108,7 +154,7 @@ class ImageProcessView : PluginFragment("ImageProcessView") {
tooltip(messages["copy"])
action {
if (showImage.get()) {
img.image.copy()
ivOutput.image.copy()
} else {
outputText.copy()
}
Expand All @@ -130,23 +176,24 @@ class ImageProcessView : PluginFragment("ImageProcessView") {
isWrapText = true
visibleWhen(!showImage)
}
img = imageview()
ivOutput = imageview()
scrollpane(true) {
visibleWhen(showImage)
content = img
content = ivOutput
}
}
}

private fun doProcess() {
if (inputText.isEmpty()) return
runAsync { controller.process(imageServiceType, inputText, fileMode.get()) } ui
runAsync { controller.process(imageServiceType, inputText, fileMode.get(), paramsMap) } ui
{
showImage.value = it !is String
when (it) {
is String -> taOutput.text = it
is ByteArray -> img.image = Image(it.inputStream())
is Image -> img.image = it
is ByteArray -> ivOutput.image = Image(it.inputStream())
is BufferedImage -> ivOutput.image = it.toFxImg()
is Image -> ivOutput.image = it
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package me.leon.img
package me.leon.toolsfx.plugin.ext

import java.awt.Color
import java.awt.image.*
import java.awt.image.BufferedImage.TYPE_INT_ARGB
import java.io.File
import javax.imageio.ImageIO
import kotlin.math.pow
import kotlin.random.Random

Expand All @@ -24,7 +26,7 @@ fun BufferedImage.gray() =
}
}

fun BufferedImage.binary(threshold: Int = 172, isGray: Boolean = true) =
fun BufferedImage.binary(threshold: Int = 159, isGray: Boolean = true) =
BufferedImage(width, height, type).apply {
repeat(width) { x ->
repeat(height) { y ->
Expand Down Expand Up @@ -379,3 +381,18 @@ operator fun BufferedImage.minus(other: BufferedImage): BufferedImage =
}
}
}

fun BufferedImage.toBinaryString(isBlackOne: Boolean): String =
StringBuilder()
.apply {
repeat(width) { x ->
repeat(height) { y ->
append(
1.takeIf { this@toBinaryString.getRGB(x, y).isWhite xor isBlackOne } ?: 0
)
}
}
}
.toString()

fun File.toBufferImage(): BufferedImage = ImageIO.read(this)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.leon.img
package me.leon.toolsfx.plugin.ext

import java.awt.Color

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package me.leon.toolsfx.plugin.ext

/**
* @author Leon
* @since 2022-12-23 15:59
* @email [email protected]
*/
enum class ColorMode {
WHITE1,
BLACK1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package me.leon.toolsfx.plugin.ext

/**
* @author Leon
* @since 2022-12-23 15:27
* @email [email protected]
*/
const val OPTIONS = "options"
const val HINT = "hint"
val IMAGE_CONFIG =
mapOf(
ImageServiceType.ZERO_ONE_QR_IMAGE to
mapOf(OPTIONS to arrayOf(ColorMode.BLACK1.toString(), ColorMode.WHITE1.toString())),
ImageServiceType.ZERO_ONE_IMAGE to
mapOf(OPTIONS to arrayOf(ColorMode.BLACK1.toString(), ColorMode.WHITE1.toString())),
ImageServiceType.IMAGE_TO_01 to
mapOf(OPTIONS to arrayOf(ColorMode.WHITE1.toString(), ColorMode.BLACK1.toString())),
ImageServiceType.IMAGE_PROCESS to
mapOf(OPTIONS to ImageOperation.values().map { it.toString() }.toTypedArray()),
ImageServiceType.MORPHOLOGY to
mapOf(
HINT to arrayOf("kernel size default is 3"),
OPTIONS to ImageMorphology.values().map { it.toString() }.toTypedArray()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package me.leon.toolsfx.plugin.ext

import me.leon.P1
import me.leon.ext.toFile

/**
* 图像形态学,需要变成二值图 需要指定kernel大小
*
* @author Leon
* @since 2022-12-23 16:48
* @email [email protected]
*/
enum class ImageMorphology : SimpleService {
ERODE {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().binary().erode(params.parseParams())
},
DILATE {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().binary().dilate(params.parseParams())
},
OPEN_OP {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().binary().openOp(params.parseParams())
},
CLOSE_OP {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().binary().closeOp(params.parseParams())
},
GRADIENT {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().binary().gradient(params.parseParams())
},
BLACK_HAT {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().binary().blackHat(params.parseParams())
},
TOP_HAT {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().binary().topHat(params.parseParams())
};

fun Map<String, String>.parseParams() = requireNotNull(this[P1]).ifEmpty { "3" }.toInt()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package me.leon.toolsfx.plugin.ext

import me.leon.ext.toFile

/**
* @author Leon
* @since 2022-12-23 16:42
* @email [email protected]
*/
enum class ImageOperation : SimpleService {
GRAY {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().gray()
},
BINARY {
override fun process(file: String, params: Map<String, String>) =
with(file.toFile().toBufferImage()) { binary(ostu()) }
},
INVERSE {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().inverse()
},
MIRROR_HEIGHT {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().mirrorHeight()
},
MIRROR_WIDTH {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().mirrorWidth()
},
MOSAIC {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().mosaic()
},
OIL_PAINT {
override fun process(file: String, params: Map<String, String>) =
file.toFile().toBufferImage().oilPaint()
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.leon.toolsfx.plugin.ext

interface ImageService {

fun process(raw: String, isFile: Boolean, params: Map<String, String>): Any
fun paramsHints(): Array<out String>
fun options(): Array<out String>
}
Loading

0 comments on commit 52b57e5

Please sign in to comment.