Skip to content

Commit

Permalink
implementation of new Kmeans API, optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
kamil-sita committed Feb 12, 2020
1 parent ffc1da9 commit 07c3431
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 176 deletions.
8 changes: 6 additions & 2 deletions src/main/java/XIS/sections/GlobalSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ public GlobalSettings() {

}

private int getNormalizedThreadCount() {
return Math.max(Math.min(Runtime.getRuntime().availableProcessors(), maxThreads), 1);
private int availableProcessors = -1;
public int getNormalizedThreadCount() {
if (availableProcessors == -1) {
availableProcessors = Math.max(Math.min(Runtime.getRuntime().availableProcessors(), maxThreads), 1);
}
return availableProcessors;
}

public ExecutorService getExecutorServiceForMostThreads() {
Expand Down
24 changes: 19 additions & 5 deletions src/main/java/XIS/sections/Interruptible.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
* Abstract class for job interruption and JavaFX progress feedback.
* Abstract class for job interruption and progress feedback. If you don't need neither use MockInterruptible class.
*/
public abstract class Interruptible {
protected boolean isInterrupted = false;
private boolean isInterrupted = false;
private UserFeedback userFeedback;
private List<Runnable> listeners = new ArrayList<>();

public abstract Runnable getRunnable();
public abstract Runnable onUninterruptedFinish();
Expand All @@ -17,12 +20,15 @@ public Interruptible() {
userFeedback = UserFeedback.getInstance();
}

public boolean isInterrupted() {
public final boolean isInterrupted() {
return isInterrupted;
}

public void interrupt() {
protected final void interrupt() {
isInterrupted = true;
for (var listener : listeners) {
listener.run();
}
}

public void reportProgress(double percentProgress) {
Expand All @@ -45,7 +51,15 @@ public void openInDefault(BufferedImage bufferedImage) {
userFeedback.openInDefault(bufferedImage);
}

public UserFeedback getUserFeedback() {
public final UserFeedback getUserFeedback() {
return userFeedback;
}

public final void addListener(Runnable listener) {
listeners.add(listener);
}

public final void removeListener(Runnable listener) {
listeners.remove(listener);
}
}
18 changes: 8 additions & 10 deletions src/main/java/XIS/sections/MockInterruptible.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,27 @@
public final class MockInterruptible extends Interruptible {
@Override
public Runnable getRunnable() {
return null;
return () -> {};
}

@Override
public Runnable onUninterruptedFinish() {
return null;
}

public boolean isInterrupted() {
return false;
}

public void interrupt() {

return () -> {};
}

@Override
public void reportProgress(double percentProgress) {

}

@Override
public void reportProgress (String message) {

}

@Override
public void popup(String message) {

}

}
31 changes: 6 additions & 25 deletions src/main/java/XIS/sections/compression/IntKMeans.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
package XIS.sections.compression;

import pl.ksitarski.simplekmeans.KMeansData;

import java.util.List;

class IntKMeans implements KMeansData {

private int val;

public IntKMeans(int val) {
this.val = val;
}

public int getVal() {
return val;
}

public void setVal(int val) {
this.val = val;
}

@Override
public double distanceTo(KMeansData kMeansData) {
return Math.abs(val - ((IntKMeans)kMeansData).getVal());
class IntKMeans {
public static double distance(int a, int b) {
return Math.abs(a - b);
}

@Override
public KMeansData meanOfList(List<KMeansData> list) {
public static int meanOfList(List<Integer> list) {
int a = 0;
for (var val : list) {
a += ((IntKMeans) val).getVal();
a += val;
}
a = a / list.size();
return new IntKMeans(a);
return a;
}
}
64 changes: 33 additions & 31 deletions src/main/java/XIS/sections/compression/LosticCompression.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package XIS.sections.compression;

import XIS.sections.GlobalSettings;
import XIS.sections.Interruptible;
import XIS.sections.MockInterruptible;
import XIS.toolset.IntegerMath;
import XIS.toolset.imagetools.YCbCrImage;
import XIS.toolset.imagetools.YCbCrLayer;
import pl.ksitarski.simplekmeans.KMeans;
import pl.ksitarski.simplekmeans.KMeansBuilder;

import java.awt.image.BufferedImage;
import java.io.IOException;
Expand All @@ -28,14 +29,6 @@ public class LosticCompression {

private static final int FLAG_NONE = 0;


/**
* compress() delegate for outside calls to the function
*/
public static Optional<CompressionOutput> compressedAndPreview(CompressionArguments compressionArguments) {
return compress(compressionArguments, new MockInterruptible());
}

public static Optional<CompressionOutput> compress(CompressionArguments arguments, Interruptible interruptible) {
var input = arguments.getInput();
if (input == null) return Optional.empty();
Expand All @@ -47,8 +40,6 @@ public static Optional<CompressionOutput> compress(CompressionArguments argument
int size_X = (int) Math.ceil(input.getWidth() / blockSize);
int size_Y = (int) Math.ceil(input.getHeight() / blockSize);

KMeans.setLogger(s -> {});

int i = 0;
for (int y = 0; y < size_Y; y++) {
for (int x = 0; x < size_X; x++) {
Expand All @@ -67,10 +58,6 @@ public static Optional<CompressionOutput> compress(CompressionArguments argument
return Optional.of(new CompressionOutput(b, preview, statistic));
}

public static Optional<BufferedImage> decompress(BitSequence bitSequence) {
return decompress(bitSequence, new MockInterruptible());
}

public static Optional<BufferedImage> decompress(BitSequence bitSequence, Interruptible interruptible) {
if (bitSequence == null) return Optional.empty();

Expand Down Expand Up @@ -143,13 +130,21 @@ private static void compressBlock(int x, int y, int size, BitSequence bitSequenc
}

private static ArrayList<Integer> generateDictionary(int x, int y, int size, YCbCrLayer layer, int k) {
ArrayList<IntKMeans> valueList = getListFromArea(x, y, size, layer);
KMeans<IntKMeans> kMeansKMeans = new KMeans<>(k, valueList);
kMeansKMeans.iterate(POST_ITERATIONS);
ArrayList<Integer> valueList = getListFromArea(x, y, size, layer);
KMeans<Integer> kMeansKMeans = new KMeansBuilder<>(
valueList,
k,
IntKMeans::meanOfList,
IntKMeans::distance
)
.setOptimizationSkipUpdatesBasedOnRange()
.setThreadCount(GlobalSettings.getInstance().getNormalizedThreadCount())
.build();
kMeansKMeans.iterateUntilStandardDeviationDeltaSmallerOrEqualTo(0.01, POST_ITERATIONS);
var results = kMeansKMeans.getCalculatedMeanPoints();
var palette = new ArrayList<Integer>();
for (var result : results) {
palette.add(result.getVal());
palette.add(result);
}

palette.sort(Integer::compareTo);
Expand Down Expand Up @@ -184,8 +179,8 @@ private static void decompressBlock(int xBlock, int yBlock, int size, BitSequenc
int yStart = yBlock * size;
int yEnd = (yBlock + 1) * size;

xEnd = xEnd > header.width ? header.width : xEnd;
yEnd = yEnd > header.height ? header.height : yEnd;
xEnd = Math.min(xEnd, header.width);
yEnd = Math.min(yEnd, header.height);

try {
for (int y = yStart; y < yEnd; y++) {
Expand All @@ -211,13 +206,13 @@ private static void decompressBlock(int xBlock, int yBlock, int size, BitSequenc
}
}

private static ArrayList<IntKMeans> getListFromArea(int x, int y, int size, YCbCrLayer layer) {
var valueList = new ArrayList<IntKMeans>(((x + 1) * y + 1) * size * size);
private static ArrayList<Integer> getListFromArea(int x, int y, int size, YCbCrLayer layer) {
var valueList = new ArrayList<Integer>(size * size);

for (int j = y * size; j < (y + 1) * size; j++) {
for (int i = x * size; i < (x + 1) * size; i++) {
if (i < layer.width() && j < layer.height()) {
valueList.add(new IntKMeans(layer.get(i, j)));
valueList.add(layer.get(i, j));
}
}
}
Expand All @@ -232,21 +227,28 @@ private static int calculateDictionarySize(int x, int y, int size, YCbCrLayer in

final int INITIAL_RESULT_COUNT = 32;

KMeans<IntKMeans> kMeans = new KMeans<>(INITIAL_RESULT_COUNT, valueList);
kMeans.iterate(PRE_ITERATIONS);
KMeans<Integer> kMeans = new KMeansBuilder<>(
valueList,
INITIAL_RESULT_COUNT,
IntKMeans::meanOfList,
IntKMeans::distance
)
.setThreadCount(GlobalSettings.getInstance().getNormalizedThreadCount())
.setOptimizationSkipUpdatesBasedOnRange()
.build();
kMeans.iterateUntilStandardDeviationDeltaSmallerOrEqualTo(0.01, PRE_ITERATIONS);
var results = kMeans.getCalculatedMeanPoints();
results.sort(Comparator.comparingInt(IntKMeans::getVal));
results.sort(Comparator.comparingInt(Integer::intValue));

List<Integer> sortedList = new ArrayList<>();

//converting from IntKMeans to Integer, removing duplicates (probably duplicates can be added, not sure)
int prevVal = -1;
for (var v : results) {
int val = v.getVal();
if (val != prevVal) {
sortedList.add(v.getVal());
if (v != prevVal) {
sortedList.add(v);
}
prevVal = val;
prevVal = v;
}

double continuity = calculateContinuity(sortedList);
Expand Down

This file was deleted.

Loading

0 comments on commit 07c3431

Please sign in to comment.