Skip to content

Commit

Permalink
Merge branch 'LuminanceClosestColour'
Browse files Browse the repository at this point in the history
  • Loading branch information
KodeMunkie committed Sep 30, 2023
2 parents 5eb26f4 + b34e510 commit 4db0e72
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 19 deletions.
10 changes: 4 additions & 6 deletions doc/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ resolution).
### Installation/Usage
If you have Java installed just double click the Img2ZXSpec.jar
file (like you would a regular .exe file). If you don't have Java
you need to get it first - <http://www.java.com/getjava>
you need to get it first - <https://www.oracle.com/java/technologies/downloads/>
The easiest way to learn how to use Image to Zx Spec is just to play with
it, you can even change most image options during image conversion.
For information on advanced features read on...
Expand Down Expand Up @@ -265,13 +265,11 @@ reduce attribute artifact edges.

#### Colour Distance Algorithm
When making a decision on which Spectrum colours best match those of the source
image's pixels a colour space "distance" is evaluated.
image's pixels an RGB colour space "distance" is evaluated.
The choice of algorithm for this evaluation is configured here - typically
the Compuphase algorithm is the best and uses heuristics based on human vision,
the Luminance or Compuphase algorithms perform the best and use heuristics based on human vision,
the classic Image to ZX Spec algorithm next (which is just a simple sum of RGB space distances),
followed by the Euclidean which is the straight line distance in RGB space. You may find
any one of these better suits your use case, typically the latter two are interchangeable however
classic tends to occasionally favour darker colours which can lead to better results.
followed by the Euclidean which is the straight line distance in RGB space.

#### Video Import Engine
By default Image to ZX Spec's uses HumbleVideo which is fast platform native video
Expand Down
6 changes: 2 additions & 4 deletions src/main/java/uk/co/silentsoftware/config/OptionsObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@
import uk.co.silentsoftware.core.converters.video.HumbleVideoImportEngine;
import uk.co.silentsoftware.core.converters.video.VLCVideoImportEngine;
import uk.co.silentsoftware.core.converters.video.VideoImportEngine;
import uk.co.silentsoftware.core.helpers.colourdistance.ClassicColourDistanceStrategy;
import uk.co.silentsoftware.core.helpers.colourdistance.ColourDistanceStrategy;
import uk.co.silentsoftware.core.helpers.colourdistance.CompuphaseColourDistanceStrategy;
import uk.co.silentsoftware.core.helpers.colourdistance.EuclideanColourDistance;
import uk.co.silentsoftware.core.helpers.colourdistance.*;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -246,6 +243,7 @@ public class OptionsObject {
private final List<ColourDistanceStrategy> colourDistancesModes;
{
colourDistancesModes = new ArrayList<>();
colourDistancesModes.add(new LuminanceColourDistance());
colourDistancesModes.add(new CompuphaseColourDistanceStrategy());
colourDistancesModes.add(new ClassicColourDistanceStrategy());
colourDistancesModes.add(new EuclideanColourDistance());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ private void orderByLuminosity(BufferedImage output1, BufferedImage output2) {
for (int x = 0; x + ATTRIBUTE_BLOCK_SIZE <= output1.getWidth() && y + ATTRIBUTE_BLOCK_SIZE <= output1.getHeight(); x += ATTRIBUTE_BLOCK_SIZE) {
int outRgb1[] = output1.getRGB(x, y, ATTRIBUTE_BLOCK_SIZE, ATTRIBUTE_BLOCK_SIZE, null, 0, ATTRIBUTE_BLOCK_SIZE);
int outRgb2[] = output2.getRGB(x, y, ATTRIBUTE_BLOCK_SIZE, ATTRIBUTE_BLOCK_SIZE, null, 0, ATTRIBUTE_BLOCK_SIZE);
float sum1 = luminositySum(outRgb1);
float sum2 = luminositySum(outRgb2);
double sum1 = luminositySum(outRgb1);
double sum2 = luminositySum(outRgb2);
if (sum1 > sum2) {
output1.setRGB(x, y, ATTRIBUTE_BLOCK_SIZE, ATTRIBUTE_BLOCK_SIZE, outRgb2, 0, ATTRIBUTE_BLOCK_SIZE);
output2.setRGB(x, y, ATTRIBUTE_BLOCK_SIZE, ATTRIBUTE_BLOCK_SIZE, outRgb1, 0, ATTRIBUTE_BLOCK_SIZE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import uk.co.silentsoftware.core.colourstrategy.ColourChoiceStrategy;
import uk.co.silentsoftware.core.converters.image.processors.GigaScreenAttribute;
import uk.co.silentsoftware.core.converters.image.processors.GigaScreenAttribute.GigaScreenColour;
import uk.co.silentsoftware.core.helpers.colourdistance.LuminanceColourDistance;

import java.awt.*;
import java.awt.image.BufferedImage;
Expand Down Expand Up @@ -142,22 +143,22 @@ public static GigaScreenAttribute.GigaScreenColour getClosestGigaScreenColour(in

/**
* Calculates the luminosity total for a set of rgb values
* based on the NTSC formula Y = 0.299*r + 0.587*g + 0.114*b.
* based on the NTSC formula
*
* @param rgbVals the rgb value sets
* @return the luminosity sum
*/
public static float luminositySum(int[] rgbVals) {
float sum = 0;
public static double luminositySum(int[] rgbVals) {
double sum = 0;
for (int rgb : rgbVals) {
int[] rgbComponents = ColourHelper.intToRgbComponents(rgb);
sum += luminosity(rgbComponents[0], rgbComponents[1], rgbComponents[2]);
}
return sum;
}

private static float luminosity(int red, int green, int blue) {
return (float)((0.299 * red) + (0.587 * green) + (0.114 * blue));
private static double luminosity(int red, int green, int blue) {
return ((LuminanceColourDistance.LUMA_RED * red) + (LuminanceColourDistance.LUMA_GREEN* green) + (LuminanceColourDistance.LUMA_BLUE * blue));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@
* Fast Euclidean colour distance calculation that doesn't consider human colour perception
*/
public class EuclideanColourDistance implements ColourDistanceStrategy {

@Override
public double getColourDistance(int red, int green, int blue, int[] colourSetComps) {
return Math.pow(red - colourSetComps[0], 2d) + Math.pow(green - colourSetComps[1], 2d) + Math.pow(blue - colourSetComps[2], 2d);
}

@Override
public String toString() {
return getCaption("adv_colour_dist_euclidean") ;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package uk.co.silentsoftware.core.helpers.colourdistance;

import static uk.co.silentsoftware.config.LanguageSupport.getCaption;

/**
* Luminance colour distance adapted from http://appleoldies.ca/a2b/DHGRColors2017.htm
*/
public class LuminanceColourDistance implements ColourDistanceStrategy {

public static final double LUMA_RED = 0.298839;
public static final double LUMA_GREEN = 0.586811;
public static final double LUMA_BLUE = 0.114350;

@Override
public double getColourDistance(int red, int green, int blue, int[] paletteComps) {
double luma1 = (red*LUMA_RED + green*LUMA_GREEN + blue*LUMA_BLUE) / (255.0*1000);
double luma2 = (paletteComps[0]*LUMA_RED + paletteComps[1]*LUMA_GREEN + paletteComps[2]*LUMA_BLUE) / (255.0*1000);
double lumaDiff = luma1-luma2;
double diffR = (paletteComps[0]-red)/255.0;
double diffG = (paletteComps[1]-green)/255.0;
double diffB = (paletteComps[2]-blue)/255.0;
return (diffR*diffR*LUMA_RED + diffG*diffG*LUMA_GREEN+ diffB*diffB*LUMA_BLUE)*0.75+ lumaDiff*lumaDiff;
}

@Override
public String toString() {
return getCaption("adv_colour_dist_luminance") ;
}
}
1 change: 1 addition & 0 deletions src/main/resources/Messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ adv_video_turbo=Turbo Mode (recommended off)
adv_colour_dist_euclidean= Euclidean Comparison
adv_colour_dist_compuphase=Compuphase Comparison
adv_colour_dist_classic=Classic Comparison
adv_colour_dist_luminance=Luminance Comparison
adv_colour_dist=Colour Distance Algorithm

#Misc Tab
Expand Down

0 comments on commit 4db0e72

Please sign in to comment.