Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch Processing and Random Augmentation for EDUX Image Augmentation #119

Merged
merged 3 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,29 @@ Edux supports a variety of image augmentations, which can be used to increase th

#### Code Example

```java
#### Single Image

````java
AugmentationSequence augmentationSequence=
new AugmentationBuilder()
.addAugmentation(new ResizeAugmentation(width*2,height*2,ResizeQuality.QUALITY))
.addAugmentation(new ContrastAugmentation(0.6f))
.addAugmentation(new BlurAugmentation(5))
.addAugmentation(new NoiseInjectionAugmentation(40))
.addAugmentation(new MonochromeAugmentation())
.addAugmentation(new FlippingAugmentation())
.addAugmentation(new ResizeAugmentation(250,250))
.addAugmentation(new ColorEqualizationAugmentation())
.addAugmentation(new CroppingAugmentation(0.2f))
.addAugmentation(new ElasticTransformationAugmentation(5,0.5))
.addAugmentation(new PerspectiveTransformationsAugmentation(Perspective.RIGHT_TILT))
.addAugmentation(new RandomDeleteAugmentation(5,20,20))
.build();

BufferedImage augmentedImage=augmentationSequence.applyTo(image);
````

#### Run for all images in a directory

```java
AugmentationSequence augmentationSequence=
new AugmentationBuilder()
.addAugmentation(new ResizeAugmentation(250,250))
.addAugmentation(new ColorEqualizationAugmentation())
.addAugmentation(new BlurAugmentation(25))
.addAugmentation(new RandomDeleteAugmentation(10,20,20))
.build()
.run(trainImagesDir,numberOfWorkers,outputDir);
```

### Battle Royale - Which algorithm is the best?
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.jvmargs=-Xmx8g
org.gradle.jvmargs=-Xmx32g
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ public void readBatchOfImages() throws Exception {
+ "augmentation-benchmark-images");
reader = new AugmentationImageReader();

var imageStream = reader.readBatchOfImages(benchmarkDataDir.toString(), 100, 100);
var imageStream = reader.readImagePathsAsStream(benchmarkDataDir.toString());
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package de.edux.augmentation.core;

import java.awt.image.BufferedImage;
import java.io.IOException;

public interface AugmentationSequence {

BufferedImage applyTo(BufferedImage image);

AugmentationSequence run(String directoryPath, int numberOfWorkers, String outputPath)
throws IOException, InterruptedException;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package de.edux.augmentation.core;

import de.edux.augmentation.io.ImageProcessingManager;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;

/** Composite augmentation that applies a sequence of augmentations. */
Expand All @@ -19,4 +21,13 @@ public BufferedImage applyTo(BufferedImage image) {
}
return currentImage;
}

@Override
public AugmentationSequence run(String directoryPath, int numberOfWorkers, String outputPath)
throws IOException, InterruptedException {
ImageProcessingManager manager =
new ImageProcessingManager(directoryPath, numberOfWorkers, this, outputPath);
manager.processImages();
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
import java.util.stream.Stream;

public class AugmentationImageReader implements IAugmentationImageReader {
public Stream<Path> readBatchOfImages(String directoryPath, int width, int height)
throws IOException {
public Stream<Path> readImagePathsAsStream(String directoryPath) throws IOException {
return Files.walk(Paths.get(directoryPath));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,5 @@

public interface IAugmentationImageReader {

/**
* Reads an image from the given path.
*
* @return The BufferedImage read from the file.
* @throws IOException If an error occurs during reading the image.
*/
Stream<Path> readBatchOfImages(String directoryPath, int width, int height) throws IOException;
Stream<Path> readImagePathsAsStream(String directoryPath) throws IOException;
}
56 changes: 56 additions & 0 deletions lib/src/main/java/de/edux/augmentation/io/ImageConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package de.edux.augmentation.io;

import de.edux.augmentation.core.AugmentationSequence;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;

public class ImageConsumer implements Runnable {
private final BlockingQueue<ImageWithName> queue;
private final AugmentationSequence augmentationSequence;
private final String outputDirectoryPath;

public ImageConsumer(
BlockingQueue<ImageWithName> queue,
AugmentationSequence augmentationSequence,
String outputDirectoryPath) {
this.queue = queue;
this.augmentationSequence = augmentationSequence;
this.outputDirectoryPath = outputDirectoryPath;
}

@Override
public void run() {
try {
while (true) {
ImageWithName image = queue.poll(2000, TimeUnit.MILLISECONDS);
BufferedImage augmentedImage = augmentationSequence.applyTo(image.image());
processImage(augmentedImage, image.fileName());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Consumer wurde unterbrochen.");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private void processImage(BufferedImage image, String fileName) throws IOException {
// Erstellen des Dateinamens mit Zeitstempel
File outputFile = Paths.get(outputDirectoryPath, fileName).toFile();

// Stellen Sie sicher, dass das Ausgabeverzeichnis existiert
File outputDir = outputFile.getParentFile();
if (!outputDir.exists()) {
outputDir.mkdirs();
}

// Speichern des Bildes
ImageIO.write(image, "png", outputFile);
System.out.println("Bild gespeichert: " + outputFile.getAbsolutePath());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package de.edux.augmentation.io;

import de.edux.augmentation.core.AugmentationSequence;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ImageProcessingManager {
private final int numberOfConsumers;
private final String inputDirectory;
private final AugmentationSequence augmentationSequence;
private final String outputDirectory;

public ImageProcessingManager(
String inputDirectoryPath,
int numberOfConsumers,
AugmentationSequence augmentationSequence,
String outputDirectory) {
this.inputDirectory = inputDirectoryPath;
this.numberOfConsumers = numberOfConsumers;
this.augmentationSequence = augmentationSequence;
this.outputDirectory = outputDirectory;
}

public void processImages() throws InterruptedException, IOException {
BlockingQueue<ImageWithName> queue = new ArrayBlockingQueue<>(1);

Thread producerThread = new Thread(new ImageProducer(queue, inputDirectory, numberOfConsumers));
producerThread.start();

ExecutorService executorService = Executors.newFixedThreadPool(numberOfConsumers);
for (int i = 0; i < numberOfConsumers; i++) {
executorService.submit(new ImageConsumer(queue, augmentationSequence, outputDirectory));
}

producerThread.join();
executorService.shutdown();
executorService.awaitTermination(2000, TimeUnit.MILLISECONDS);
}
}
45 changes: 45 additions & 0 deletions lib/src/main/java/de/edux/augmentation/io/ImageProducer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package de.edux.augmentation.io;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.BlockingQueue;
import javax.imageio.ImageIO;

public class ImageProducer implements Runnable {

private final BlockingQueue<ImageWithName> queue;
private final String directoryPathString;
private final int numberOfConsumers;
private AugmentationImageReader augmentationImageReader = new AugmentationImageReader();

public ImageProducer(
BlockingQueue<ImageWithName> queue, String directoryPathString, int numberOfConsumers)
throws IOException {
this.queue = queue;
this.directoryPathString = directoryPathString;
this.numberOfConsumers = numberOfConsumers;
}

@Override
public void run() {
try {
this.augmentationImageReader
.readImagePathsAsStream(directoryPathString)
.forEach(
path -> {
try (InputStream is = Files.newInputStream(Paths.get(path.toString()))) {
queue.put(new ImageWithName(ImageIO.read(is), path.getFileName().toString()));
} catch (AccessDeniedException ex) {
System.out.println("Access denied: " + path.toString());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
5 changes: 5 additions & 0 deletions lib/src/main/java/de/edux/augmentation/io/ImageWithName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.edux.augmentation.io;

import java.awt.image.BufferedImage;

public record ImageWithName(BufferedImage image, String fileName) {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import de.edux.augmentation.core.AugmentationSequence;
import de.edux.augmentation.effects.geomentry.Perspective;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -21,7 +22,8 @@ public class AllAugmentations {

@Test
void shouldApplyAllAugmentations() throws InterruptedException, IOException {
var originalImage = loadTestImage("augmentation/national-park.png");
var originalImage = loadTestImage("augmentation" + File.separator + "national-park.png");

int width = originalImage.getWidth();
int height = originalImage.getHeight();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import de.edux.augmentation.core.AugmentationBuilder;
import de.edux.augmentation.core.AugmentationSequence;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -22,7 +23,7 @@ public class AugmentationEffectsTest {

@Test
void shouldApplyAugmentationSequenceOnSingleImage() throws IOException, InterruptedException {
var image = loadTestImage("augmentation/edux-original_3.png");
var image = loadTestImage("augmentation" + File.separator + "edux-original_3.png");

AugmentationSequence augmentationSequence =
new AugmentationBuilder()
Expand Down Expand Up @@ -53,7 +54,7 @@ void shouldApplyAugmentationSequenceOnSingleImage() throws IOException, Interrup

@Test
void shouldLookLikeRetroPhoto() throws IOException, InterruptedException {
var originalImage = loadTestImage("augmentation/human-realistic.png");
var originalImage = loadTestImage("augmentation" + File.separator + "human-realistic.png");
int width = originalImage.getWidth();
int height = originalImage.getHeight();

Expand Down Expand Up @@ -91,7 +92,7 @@ void shouldLookLikeRetroPhoto() throws IOException, InterruptedException {

@Test
void shouldApplyBlurAugmentation() throws IOException, InterruptedException {
var originalImage = loadTestImage("augmentation/fireworks.png");
var originalImage = loadTestImage("augmentation" + File.separator + "fireworks.png");
int width = originalImage.getWidth();
int height = originalImage.getHeight();

Expand Down Expand Up @@ -123,7 +124,7 @@ void shouldApplyBlurAugmentation() throws IOException, InterruptedException {

@Test
void shouldApplyCroppingAugmentation() throws IOException, InterruptedException {
var image = loadTestImage("augmentation/edux-original_3.png");
var image = loadTestImage("augmentation" + File.separator + "edux-original_3.png");

int width = image.getWidth();
int height = image.getHeight();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import de.edux.augmentation.core.AugmentationBuilder;
import de.edux.augmentation.core.AugmentationSequence;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -19,7 +20,7 @@ class BlurAugmentationTest {

@Test
void apply() throws IOException, InterruptedException {
var image = loadTestImage("augmentation/fireworks.png");
var image = loadTestImage("augmentation" + File.separator + "fireworks.png");
int originalWidth = image.getWidth();
int originalHeight = image.getHeight();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import de.edux.augmentation.core.AugmentationBuilder;
import de.edux.augmentation.core.AugmentationSequence;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -19,7 +20,7 @@ class ColorEqualizationAugmentationTest {

@Test
void apply() throws IOException, InterruptedException {
var image = loadTestImage("augmentation/national-park.png");
var image = loadTestImage("augmentation" + File.separator + "national-park.png");
int originalWidth = image.getWidth();
int originalHeight = image.getHeight();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import de.edux.augmentation.core.AugmentationBuilder;
import de.edux.augmentation.core.AugmentationSequence;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -19,7 +20,7 @@ public class ElasticTransformationTest {

@Test
void shouldApplyAugmentationSequenceOnSingleImage() throws IOException, InterruptedException {
var image = loadTestImage("augmentation/edux-original_3.png");
var image = loadTestImage("augmentation" + File.separator + "edux-original_3.png");

AugmentationSequence augmentationSequence =
new AugmentationBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import de.edux.augmentation.core.AugmentationSequence;
import de.edux.augmentation.effects.geomentry.Perspective;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -26,7 +27,9 @@ void setUp() throws IOException {}

@Test
void shouldApplyAugmentationSequenceOnSingleImage() throws IOException, InterruptedException {
var image = AugmentationTestUtils.loadTestImage("augmentation/edux-original_3.png");
var image =
AugmentationTestUtils.loadTestImage(
"augmentation" + File.separator + "edux-original_3.png");

double cosAngle = Math.cos(Math.toRadians(30)); // 30 Grad Rotation
double sinAngle = Math.sin(Math.toRadians(30));
Expand Down
Loading