Skip to content

Commit

Permalink
Merge pull request #11 from seujorgenochurras/screen-parse
Browse files Browse the repository at this point in the history
Major optimization, 10x faster
  • Loading branch information
seujorgenochurras authored Feb 19, 2024
2 parents da710fd + 80afc54 commit 5a62663
Show file tree
Hide file tree
Showing 17 changed files with 209 additions and 110 deletions.
10 changes: 4 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,30 @@ plugins {
id("maven-publish")
id("signing")
}

val currentGroup = "io.github.seujorgenochurras"
val currentVersion = "0.0.2"

repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
implementation("io.metaloom.video:video4j:1.3.0")
implementation("org.openpnp:opencv:4.6.0-0")
}
tasks.test {
useJUnitPlatform()
}
tasks.withType(Jar::class) {
manifest {
attributes["Manifest-Version"] = "1.0"
attributes["Main-Class"] = "io.github.seujorgenochurras.ScreenParser"
}
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
groupId = currentGroup
groupId = "io.github.seujorgenochurras"
artifactId = "image-to-ascii"
version = currentVersion
version = "0.0.2"
from(components["java"])

pom {
Expand Down
Binary file added gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
1 change: 0 additions & 1 deletion src/main/java/io/github/seujorgenochurras/Main.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.github.seujorgenochurras;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

Expand Down
76 changes: 76 additions & 0 deletions src/main/java/io/github/seujorgenochurras/ScreenParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.github.seujorgenochurras;

import io.github.seujorgenochurras.color.BestSymbolPatternFinder;
import io.github.seujorgenochurras.image.BetterImage;
import io.github.seujorgenochurras.image.ascii.AsciiParser;
import io.github.seujorgenochurras.image.ascii.ParserBuilder;
import io.github.seujorgenochurras.image.ascii.ParserConfig;
import io.github.seujorgenochurras.image.ascii.algorithm.pixel.bright.Algorithms;
import io.github.seujorgenochurras.image.ascii.algorithm.pixel.color.DefaultColorType;

import java.awt.*;
import java.util.Timer;
import java.util.TimerTask;

import static io.github.seujorgenochurras.util.StringUtils.getUTFChars;

public class ScreenParser {
public static final String[] symbols = BestSymbolPatternFinder.
findBestPattern(2, 55, getUTFChars(32, 126)).toArray();


public static final ParserConfig defaultParserConfig = ParserBuilder.startBuild()
.symbols(symbols)
.scaled()
.height(80)
.width(400)
.getScale()
.parserAlgorithm(Algorithms.HUMAN_EYE_ALGORITHM.getAlgorithm())
.colorAlgorithm(DefaultColorType.ANSI)
.build();


public static void main(String[] args) throws AWTException {
if (args[0] == null || args[1] == null) {
args[0] = "80";
args[1] = "300";
}

final ParserConfig defaultParserConfig = ParserBuilder.startBuild()
.symbols(symbols)
.scaled()
.height(Integer.parseInt(args[1]))
.width(Integer.parseInt(args[0]))
.getScale()
.parserAlgorithm(Algorithms.HUMAN_EYE_ALGORITHM.getAlgorithm())
// .colorAlgorithm(DefaultColorType.ANSI)
.build();


Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle screenRect = new Rectangle(screenDimension);
Robot robot = new Robot();
System.out.print("\u001b[2J");
final double[] fps = {1};
final int[] framesThisSecond = {0};
TimerTask task = new TimerTask() {
@Override
public void run() {
fps[0] = framesThisSecond[0];
framesThisSecond[0] = 0;

}
};
Timer timer = new Timer("Timer");
int delay = 1000;
timer.scheduleAtFixedRate(task, 0, delay);

while (true) {
framesThisSecond[0]++;
System.out.print("\u001B[1;1H");
System.out.print(AsciiParser.parse(new BetterImage(robot.createScreenCapture(screenRect)), defaultParserConfig));
System.out.println("\u001B[0;1000H Current fps: " + fps[0]);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static SymbolList findBestPattern(String... chars) {
}

public static SymbolList findBestPattern(int maxSymbols, String... chars) {

return findBestPattern(10, maxSymbols, chars);
}

Expand All @@ -33,6 +34,8 @@ public static SymbolList findBestPattern(int symbolAccuracy, int maxSymbols, Str

SymbolList symbolList = new SymbolList(maxSymbols, symbolAccuracy);
for (String symbol : chars) {
if (symbolList.size() > maxSymbols) break;

g2d = image.getGraphics();

Font font = new Font("Fira Mono", Font.BOLD, 40);
Expand Down Expand Up @@ -67,7 +70,9 @@ private static double getAvgPixelBrightness(BufferedImage image) {
int red = color.getRed().getColorValue();
int green = color.getGreen().getColorValue();
int blue = color.getBlue().getColorValue();
avgPixelBrightness.addAndGet(Algorithms.BRIGHTEST_PIXEL.getAlgorithm().getPixelRepresentation(red, green, blue));

long pixelBrightness = Algorithms.BRIGHTEST_PIXEL.getAlgorithm().getPixelRepresentation(red, green, blue);
avgPixelBrightness.addAndGet(pixelBrightness);

});
return (double) avgPixelBrightness.get() / betterImage.getPixels().size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ public SymbolList(int listSize) {
this.sortedSymbols = new Symbol[listSize - 1];
}

public SymbolList(int listSize, int acceptableCharAccurace) {
public SymbolList(int listSize, int acceptableCharAccuracy) {
this.sortedSymbols = new Symbol[listSize - 1];
this.acceptableSymbolAccuracy = acceptableCharAccurace;
System.out.println(sortedSymbols.length);
this.acceptableSymbolAccuracy = acceptableCharAccuracy;

}


public void addSymbol(Symbol symbol) {
double newSymbolBrightness = symbol.getBrightness();

int symbolShade = (int) Math.round(newSymbolBrightness / ((double) 256 / sortedSymbols.length));
int symbolShade = (int) Math.round(newSymbolBrightness / ((double) 255 / sortedSymbols.length));
if (sortedSymbols.length - 1 < symbolShade) return;


Expand Down Expand Up @@ -58,11 +59,11 @@ public Symbol[] getSymbolsAsArray() {
public String[] toArray() {
List<String> stringList = new ArrayList<>();

for (int i = 0; i < sortedSymbols.length - 1; i++) {
for (int i = 0; i <= sortedSymbols.length - 1; i++) {
if (sortedSymbols[i] == null) continue;
stringList.add(sortedSymbols[i].getData());
}
return stringList.toArray(new String[stringList.size() - 1]);
return stringList.toArray(new String[stringList.size()]);
}

public int size() {
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/io/github/seujorgenochurras/demo/ImageToAscii.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@
public class ImageToAscii {

private static final String[] unorderedSymbols = getUTFChars(32, 123);
private static final String[] symbols = BestSymbolPatternFinder.findBestPattern(1, unorderedSymbols).toArray();
private static final String[] symbols = BestSymbolPatternFinder.findBestPattern(100, unorderedSymbols).toArray();


private static final ParserConfig parserConfig = ParserBuilder.startBuild()
.parserAlgorithm(Algorithms.HUMAN_EYE_ALGORITHM)
.scaled()
.height(50)
.width(100)
.height(50)
.width(100)
.getScale()
.symbols(symbols)
.colorAlgorithm(new AnsiColorAlgorithm())
.build();
private static PixelColor[] tones;

public static void main(String[] args) throws IOException {
asciifyFile("src/main/resources/image/jorge.png");
asciifyFile("/home/thiagoelias/IdeaProjects/image-to-ascii/src/main/resources/image/car.png");
}

public static void asciifyFile(String fileName) throws IOException {
Expand All @@ -43,7 +43,7 @@ public static void asciifyFile(String fileName) throws IOException {

String asciiArt = AsciiParser.parse(betterImage, parserConfig);

File newFile = new File("/home/thiago/Desktop/projects/image-to-ascii/src/main/resources/" + image.getName().replaceAll("png|jpg|jpeg", "txt"));
File newFile = new File("myAscii.txt");
FileWriter fileWriter = new FileWriter(newFile);
fileWriter.write(asciiArt);
fileWriter.flush();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
import io.github.seujorgenochurras.image.ascii.ParserConfig;
import io.github.seujorgenochurras.image.ascii.algorithm.pixel.bright.Algorithms;
import io.github.seujorgenochurras.image.ascii.algorithm.pixel.color.DefaultColorType;
import io.github.seujorgenochurras.video.buffer.BufferedAsciiVideoParser;
import io.github.seujorgenochurras.video.buffer.PixelAnalyzerBuffer;
import io.metaloom.video4j.Video4j;
import io.metaloom.video4j.VideoFile;
import io.metaloom.video4j.Videos;
import nu.pattern.OpenCV;
import org.opencv.videoio.VideoCapture;

import static io.github.seujorgenochurras.util.StringUtils.getUTFChars;

Expand All @@ -35,33 +33,42 @@ public class VideoToAsciiAnimation {
.build();

public static void main(String[] args) throws InterruptedException {
Video4j.init();
VideoFile videoFile = Videos.open(VIDEO_PATH);

PixelAnalyzerBuffer pixelAnalyzerBuffer = new PixelAnalyzerBuffer(videoFile, 25);
System.loadLibrary("ffmepg");

pixelAnalyzerBuffer.startQueue();

var bufferedAsciiVideoParser = new BufferedAsciiVideoParser(parserConfig, pixelAnalyzerBuffer, 10);
OpenCV.loadShared();
OpenCV.loadLocally();
Video4j.init();
VideoCapture a = new VideoCapture(VIDEO_PATH);
Thread.sleep(4000);
System.out.println(a.isOpened());

bufferedAsciiVideoParser.startQueue();

System.out.print("\u001B[0J");
Thread.sleep(5000);
long videoLength = pixelAnalyzerBuffer.getVideoFileLength();
new Thread(() -> {
for (int i = 0; i < videoLength; i++) {
try {
Thread.sleep(1000 / DESIRED_FPS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
String asciiArt = bufferedAsciiVideoParser.getAsciiArtQueue().poll();
if (asciiArt == null) continue;
System.out.print("\u001B[1;1H");
System.out.print(asciiArt);
}
}).start();
// VideoFile videoFile = Videos.get(VIDEO_PATH);
//
//
// PixelAnalyzerBuffer pixelAnalyzerBuffer = new PixelAnalyzerBuffer(videoFile, 25);
//
// pixelAnalyzerBuffer.startQueue();
//
// var bufferedAsciiVideoParser = new BufferedAsciiVideoParser(parserConfig, pixelAnalyzerBuffer, 10);
//
// bufferedAsciiVideoParser.startQueue();
//
// System.out.print("\u001B[0J");
// long videoLength = pixelAnalyzerBuffer.getVideoFileLength();
// new Thread(() -> {
// for (int i = 0; i < videoLength; i++) {
// try {
// Thread.sleep(1000 / DESIRED_FPS);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// String asciiArt = bufferedAsciiVideoParser.getAsciiArtQueue().poll();
// if (asciiArt == null) continue;
// System.out.print("\u001B[1;1H");
// System.out.print(asciiArt);
// }
// }).start();

}

Expand Down
24 changes: 5 additions & 19 deletions src/main/java/io/github/seujorgenochurras/image/BetterImage.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package io.github.seujorgenochurras.image;

import io.github.seujorgenochurras.image.pixel.ImagePixel;
import io.github.seujorgenochurras.image.pixel.ImagePixelGroup;
import io.github.seujorgenochurras.image.pixel.PixelBuilder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
Expand All @@ -16,17 +14,16 @@ public class BetterImage {

public BetterImage(BufferedImage bufferedImage) {
this.bufferedImage = bufferedImage;
pixels = PixelBuilder.build(this);
this.pixels = new ImagePixelGroup(bufferedImage);
}

public BetterImage(String imagePath){
this.bufferedImage = tryGenerateImageFromPath(imagePath);
pixels = PixelBuilder.build(this);
public BetterImage(String imagePath) {
this.bufferedImage = tryGenerateImageFromPath(imagePath);
this.pixels = new ImagePixelGroup(bufferedImage);
}

public BufferedImage tryGenerateImageFromPath(String imagePath) {
File imageFile = new File(imagePath);

try {
return ImageIO.read(imageFile);
} catch (IOException e) {
Expand All @@ -39,15 +36,6 @@ public BufferedImage getBufferedImage() {
return bufferedImage;
}

public void updateCurrentPixels() {
this.setPixels(pixels);
}

public void setPixel(ImagePixel imagePixel) {
bufferedImage.setRGB(imagePixel.x, imagePixel.y, imagePixel.color.getRGB());

}

public int getWidth() {
return bufferedImage.getWidth();
}
Expand All @@ -60,7 +48,5 @@ public ImagePixelGroup getPixels() {
return pixels;
}

public void setPixels(ImagePixelGroup pixels) {
pixels.forEach(this::setPixel);
}

}
Loading

0 comments on commit 5a62663

Please sign in to comment.