Browse Source

Reorganized a few more files, added a proper searched listener to the GUI to show the number of files to be processed before the actual processing starts.

master
Drew Short 10 years ago
parent
commit
50f87a51ac
  1. 20
      engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
  2. 40
      engine/src/main/scala/com/sothr/imagetools/engine/Engine.scala
  3. 2
      engine/src/main/scala/com/sothr/imagetools/engine/SequentialEngine.scala
  4. 36
      gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala
  5. 67
      gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala

20
engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala

@ -20,7 +20,7 @@ class ConcurrentEngine extends Engine with grizzled.slf4j.Logging {
implicit val timeout = Timeout(30, TimeUnit.SECONDS) implicit val timeout = Timeout(30, TimeUnit.SECONDS)
override def setSearchedListener(listenerRef: ActorRef) = { override def setSearchedListener(listenerRef: ActorRef) = {
this.searchedListener = listenerRef;
} }
override def setProcessedListener(listenerRef: ActorRef) = { override def setProcessedListener(listenerRef: ActorRef) = {
@ -61,29 +61,15 @@ class ConcurrentEngine extends Engine with grizzled.slf4j.Logging {
} }
val f = engineSimilarityController ? EngineGetSimilarityResults val f = engineSimilarityController ? EngineGetSimilarityResults
val result = Await.result(f, timeout.duration).asInstanceOf[List[SimilarImages]] val result = Await.result(f, timeout.duration).asInstanceOf[List[SimilarImages]]
//process the result into a list we want in cleanedSimilarImages
var count = 0
val cleanedSimilarImages = new mutable.MutableList[SimilarImages]()
val ignoreSet = new mutable.HashSet[Image]()
for (similarImages <- result) {
count += 1
if (count % 25 == 0 || count == result.length) debug(s"Cleaning similar image $count/${result.length} ${result.length - count} left to clean")
if (!ignoreSet.contains(similarImages.rootImage)) {
cleanedSimilarImages += similarImages
ignoreSet += similarImages.rootImage
for (image <- similarImages.similarImages) {
ignoreSet += image
}
}
}
val cleanedSimilarImages = this.processSimilarities(result)
var similarCount = 0 var similarCount = 0
for (similarImage <- cleanedSimilarImages) { for (similarImage <- cleanedSimilarImages) {
similarCount += 1 + similarImage.similarImages.size similarCount += 1 + similarImage.similarImages.size
} }
info(s"Finished processing ${images.size} images. Found $similarCount similar images") info(s"Finished processing ${images.size} images. Found $similarCount similar images")
cleanedSimilarImages.toList
cleanedSimilarImages
} }
def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = { def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = {

40
engine/src/main/scala/com/sothr/imagetools/engine/Engine.scala

@ -40,13 +40,16 @@ abstract class Engine extends Logging {
val files = directory.listFiles(imageFilter) val files = directory.listFiles(imageFilter)
if (files != null) { if (files != null) {
fileList ++= files fileList ++= files
info(s"Found ${files.length} files that are images in directory: $directoryPath")
debug(s"Found ${files.length} files that are images in directory: $directoryPath")
if (recursive) { if (recursive) {
val directoryFilter = new DirectoryFilter val directoryFilter = new DirectoryFilter
val directories = directory.listFiles(directoryFilter) val directories = directory.listFiles(directoryFilter)
for (directory <- directories) { for (directory <- directories) {
fileList ++= getAllImageFiles(directory.getAbsolutePath, recursive, recursiveDepth - 1) fileList ++= getAllImageFiles(directory.getAbsolutePath, recursive, recursiveDepth - 1)
this.searchedListener ! SubmitMessage(s"Found ${fileList.length} files to process")
} }
} else {
this.searchedListener ! SubmitMessage(s"Found ${fileList.length} files to process")
} }
} }
} }
@ -73,6 +76,41 @@ abstract class Engine extends Logging {
deleteImage(image) deleteImage(image)
} }
} }
/**
* Go through the list of similarities and group them so that they represent actual similarities
*
* For example. A is similar to B and C is similar to B but A is was not considered similar to C. Therefore A B and C should be considered similar unless otherwise noted.
*
* @param similarList a list of detected similar images
* @return a grouped and combined list of similar images
*/
def processSimilarities(similarList: List[SimilarImages]): List[SimilarImages] = {
//process the result into a list we want in cleanedSimilarImages
var count = 0
val cleanedSimilarImages = new mutable.MutableList[SimilarImages]()
val ignoreSet = new mutable.HashSet[Image]()
for (similarImages <- similarList) {
count += 1
if (count % 25 == 0 || count == similarList.length) debug(s"Cleaning similar image $count/${similarList.length} ${similarList.length - count} left to clean")
if (!ignoreSet.contains(similarImages.rootImage)) {
cleanedSimilarImages += similarImages
ignoreSet += similarImages.rootImage
for (image <- similarImages.similarImages) {
ignoreSet += image
}
}
//rewrite todo:
/*
Go through all the images. If a similar image for the image doesn't exist, create it. if it does,
add it to that similar image. The end result is that all images should be grouped according to
their similar images and images that are similar to them.
*/
}
//return a non mutable cleaned list
cleanedSimilarImages.toList
}
} }
case class SubmitMessage(message: String) case class SubmitMessage(message: String)

2
engine/src/main/scala/com/sothr/imagetools/engine/SequentialEngine.scala

@ -66,7 +66,7 @@ class SequentialEngine extends Engine with Logging {
} }
} }
info(s"Finished processing ${images.size} images. Found $similarCount similar images") info(s"Finished processing ${images.size} images. Found $similarCount similar images")
allSimilarImages.toList
this.processSimilarities(allSimilarImages.toList)
} }
def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = { def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = {

36
gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala

@ -165,24 +165,6 @@ class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) exte
this.selectedIndexes.add(0) this.selectedIndexes.add(0)
} }
private def clearSelectionFormatting() = {
val iterator = this.parentTilePane.getChildren.iterator()
while (iterator.hasNext) {
//remove the selection styling
val imageTile: VBox = iterator.next().asInstanceOf[VBox]
imageTile.setBorder(Border.EMPTY)
}
}
private def setSelectionFormatting(index: Int): Unit = {
setSelectionFormatting(this.parentTilePane.getChildren.get(index).asInstanceOf[ImageTile])
}
private def setSelectionFormatting(imageTile: ImageTile) = {
val borderStroke = new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN)
imageTile.asInstanceOf[VBox].setBorder(new Border(borderStroke))
}
override def selectLast(): Unit = { override def selectLast(): Unit = {
clearSelectionFormatting clearSelectionFormatting
this.selectedIndexes.clear() this.selectedIndexes.clear()
@ -239,6 +221,15 @@ class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) exte
} }
} }
private def setSelectionFormatting(index: Int): Unit = {
setSelectionFormatting(this.parentTilePane.getChildren.get(index).asInstanceOf[ImageTile])
}
private def setSelectionFormatting(imageTile: ImageTile) = {
val borderStroke = new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN)
imageTile.asInstanceOf[VBox].setBorder(new Border(borderStroke))
}
override def select(obj: ImageTile): Unit = { override def select(obj: ImageTile): Unit = {
if (this.parentTilePane.getChildren.contains(obj)) { if (this.parentTilePane.getChildren.contains(obj)) {
clearSelectionFormatting clearSelectionFormatting
@ -248,6 +239,15 @@ class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) exte
} }
} }
private def clearSelectionFormatting() = {
val iterator = this.parentTilePane.getChildren.iterator()
while (iterator.hasNext) {
//remove the selection styling
val imageTile: VBox = iterator.next().asInstanceOf[VBox]
imageTile.setBorder(Border.EMPTY)
}
}
override def isEmpty: Boolean = { override def isEmpty: Boolean = {
this.parentTilePane.getChildren.isEmpty this.parentTilePane.getChildren.isEmpty
} }

67
gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala

@ -66,6 +66,7 @@ class AppController extends Logging {
// configure the listener // configure the listener
guiListener ! SetupListener(progressBar, progressLabel) guiListener ! SetupListener(progressBar, progressLabel)
// tell the engine to use our listener // tell the engine to use our listener
this.engine.setSearchedListener(guiListener)
this.engine.setProcessedListener(guiListener) this.engine.setProcessedListener(guiListener)
this.engine.setSimilarityListener(guiListener) this.engine.setSimilarityListener(guiListener)
// Initialize the progress label // Initialize the progress label
@ -282,44 +283,12 @@ class AppController extends Logging {
} }
} }
@FXML
def showSimilarImages(event: ActionEvent) = {
resetPaginator()
imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
val f: Future[List[Image]] = Future {
val similarImages = engine.getSimilarImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
val tempImages = new mutable.MutableList[Image]()
for (similarImage <- similarImages) {
debug(s"Adding similar images ${similarImage.rootImage.toString} to app")
tempImages += similarImage.rootImage
similarImage.similarImages.foreach(image => tempImages += image)
}
tempImages.toList
}
f onComplete {
case Success(images) =>
info(s"Displaying ${images.length} similar images")
Platform.runLater(new Runnable() {
override def run() {
setPagesContent(images)
showPage(0)
}
})
case Failure(t) =>
error("An Error Occurred", t)
}
}
//endregion
def resetPaginator() = { def resetPaginator() = {
this.paginator.setDisable(true) this.paginator.setDisable(true)
this.paginator.setPageCount(1) this.paginator.setPageCount(1)
} }
//todo: include a templating engine for rendering information
//endregion
def setPagesContent(images: List[Image]) = { def setPagesContent(images: List[Image]) = {
this.currentImages = images this.currentImages = images
@ -330,6 +299,8 @@ class AppController extends Logging {
this.paginator.setDisable(false) this.paginator.setDisable(false)
} }
//todo: include a templating engine for rendering information
def showPage(pageIndex: Integer) = { def showPage(pageIndex: Integer) = {
val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt
val startIndex = pageIndex * itemsPerPage val startIndex = pageIndex * itemsPerPage
@ -353,6 +324,36 @@ class AppController extends Logging {
this.imageTilePane this.imageTilePane
} }
@FXML
def showSimilarImages(event: ActionEvent) = {
resetPaginator()
imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
val f: Future[List[Image]] = Future {
val similarImages = engine.getSimilarImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
val tempImages = new mutable.MutableList[Image]()
for (similarImage <- similarImages) {
debug(s"Adding similar images ${similarImage.rootImage.toString} to app")
tempImages += similarImage.rootImage
similarImage.similarImages.foreach(image => tempImages += image)
}
tempImages.toList
}
f onComplete {
case Success(images) =>
info(s"Displaying ${images.length} similar images")
Platform.runLater(new Runnable() {
override def run() {
setPagesContent(images)
showPage(0)
}
})
case Failure(t) =>
error("An Error Occurred", t)
}
}
/** /**
* Show a plain text utility dialog * Show a plain text utility dialog
* *

Loading…
Cancel
Save