From fb251c9df8280dc9531be0993f7892d28e25696a Mon Sep 17 00:00:00 2001 From: Lennart Eichhorn <lennart@madmanfred.com> Date: Sun, 31 May 2020 17:45:17 +0200 Subject: [PATCH] Video generation is now done by a visualizer. --- .../java/MainClasses/GeneticAlgorithm.java | 4 + src/main/java/MainClasses/Main.java | 9 - .../java/Visualization/JCodecPNGtoMP4.java | 4 +- .../Visualizers/BestFoldingsToVideo.java | 227 ++++++++++++++++++ 4 files changed, 233 insertions(+), 11 deletions(-) create mode 100644 src/main/java/Visualization/Visualizers/BestFoldingsToVideo.java diff --git a/src/main/java/MainClasses/GeneticAlgorithm.java b/src/main/java/MainClasses/GeneticAlgorithm.java index 16f6c3d..dd03c4e 100644 --- a/src/main/java/MainClasses/GeneticAlgorithm.java +++ b/src/main/java/MainClasses/GeneticAlgorithm.java @@ -14,6 +14,7 @@ import Selectors.Tournament; import Visualization.Visualizers.BestFoldingToConsole; import Visualization.Visualizers.BestFoldingToImage; +import Visualization.Visualizers.BestFoldingsToVideo; import Visualization.Visualizers.GenerationOverviewToConsole; import Visualization.Visualizers.GenerationProgressToLog; import java.io.IOException; @@ -92,6 +93,9 @@ public class GeneticAlgorithm { }else if (vm.equals(VisualizerMethods.Generation)) { this.visualizers[j] = new GenerationOverviewToConsole(isHydrophobic, config); j++; + }else if (vm.equals(VisualizerMethods.Video)) { + this.visualizers[j] = new BestFoldingsToVideo(config); + j++; } } diff --git a/src/main/java/MainClasses/Main.java b/src/main/java/MainClasses/Main.java index d063f48..ffa497e 100644 --- a/src/main/java/MainClasses/Main.java +++ b/src/main/java/MainClasses/Main.java @@ -16,14 +16,5 @@ public class Main { GeneticAlgorithm ga = new GeneticAlgorithm(protein, config); ga.simulateGenerations(); - // Create a new video if possible and desired - boolean imagesRefreshed = Arrays.asList(config.getVisualizers()).contains(VisualizerMethods.Image); - boolean videoEnabled = Arrays.asList(config.getVisualizers()).contains(VisualizerMethods.Video); - if (imagesRefreshed && videoEnabled){ - String videoPath = config.getVideoDirectory() + "/" + config.getJobName() + ".mp4"; - VideoCreator.createVideo(config.getImageSequenceDirectory(), videoPath, - config.getImageFps(), config.getImagesToFpsIncrease(), config.getImageFpsMax(), - ga.getMaxH(), ga.getMaxW(), config.isZoom()); - } } } diff --git a/src/main/java/Visualization/JCodecPNGtoMP4.java b/src/main/java/Visualization/JCodecPNGtoMP4.java index bb6d05e..0b730a2 100644 --- a/src/main/java/Visualization/JCodecPNGtoMP4.java +++ b/src/main/java/Visualization/JCodecPNGtoMP4.java @@ -41,7 +41,7 @@ import java.util.List; */ public class JCodecPNGtoMP4 { - static void sortByNumber(File[] files) { + public static void sortByNumber(File[] files) { Arrays.sort(files, new Comparator<File>() { @Override public int compare(File o1, File o2) { @@ -64,7 +64,7 @@ public class JCodecPNGtoMP4 { }); } - static void generateVideoBySequenceImages(File videoFile, String pathImages, String imageExt, + public static void generateVideoBySequenceImages(File videoFile, String pathImages, String imageExt, int fps, int imgToFpsIncrease, int maxFps) throws Exception { SeekableByteChannel out = null; try { diff --git a/src/main/java/Visualization/Visualizers/BestFoldingsToVideo.java b/src/main/java/Visualization/Visualizers/BestFoldingsToVideo.java new file mode 100644 index 0000000..12b18fd --- /dev/null +++ b/src/main/java/Visualization/Visualizers/BestFoldingsToVideo.java @@ -0,0 +1,227 @@ +package Visualization.Visualizers; + +import Interfaces.Visualizer; +import MainClasses.Candidate; +import MainClasses.Config; +import MainClasses.GeneticAlgorithm; +import Visualization.JCodecPNGtoMP4; +import Visualization.VideoCreator; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import javax.imageio.ImageIO; + +public class BestFoldingsToVideo implements Visualizer { + + Config config; + private final int MAXIMUM_SIZE; + private final FilenameFilter imageFilter; + + public BestFoldingsToVideo(Config config){ + this.config = config; + MAXIMUM_SIZE = 3000; + imageFilter = new FilenameFilter() { + @Override + public boolean accept(final File dir, String name) { + for (final String ext : new String[]{"jpg", "png"}) { + if (name.endsWith("." + ext) && name.startsWith(config.getJobName())) { + return (true); + } + } + return (false); + } + }; + try { + Files.createDirectories(Paths.get(config.getVideoDirectory())); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + + @Override + public void drawProtein(Candidate[] generation, GeneticAlgorithm geneticAlgorithm) { + //TODO Only render video for the last generation + if(geneticAlgorithm.generation == config.getTotalGenerations()-2){ + String filename = config.getLogfileDirectory() + "/" + config.getJobName() + ".mp4"; + createVideo( + config.getImageSequenceDirectory(), + filename, + config.getImageFps(), + config.getImagesToFpsIncrease(), + config.getImageFpsMax(), + geneticAlgorithm.getMaxH(), + geneticAlgorithm.getMaxW(), + config.isZoom() + ); + } + } + + public void createVideo(String imagesPath, String videoPathAndFile, + int imgFps, int imgToFpsIncrease, int maxFps, int maxH, int maxW, boolean zoom) { + try { + System.err.println("\nStarting image resizing"); + if (zoom) { + resizeImagesWithZoom(imagesPath); + } else { + resizeImages(imagesPath, maxH, maxW); + } + + File videoFile = new File(videoPathAndFile); + if (!videoFile.exists()) { + videoFile.createNewFile(); + } + + try { + JCodecPNGtoMP4.generateVideoBySequenceImages(videoFile, imagesPath, "png", imgFps, imgToFpsIncrease, maxFps); + } catch (Exception e) { + e.printStackTrace(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void resizeImages(String imagesPath, int maxH, int maxW) throws IOException { + File dir = new File(imagesPath); + + if (dir.isDirectory()) { + // reads input images and determines maximum required size + int maxHeight = maxH; + int maxWidth = maxW; + + if (maxHeight <= 0 || maxWidth <= 0) { + int counter = 1; + for (final File f : dir.listFiles(imageFilter)) { + BufferedImage inputImage = ImageIO.read(f); + if (maxHeight < inputImage.getHeight()) { + maxHeight = inputImage.getHeight(); + } + if (maxWidth < inputImage.getWidth()) { + maxWidth = inputImage.getWidth(); + } + } + } + + // Needed because video is not playable with bigger sizes, mostly a concern for straight line initialization + if (maxWidth > MAXIMUM_SIZE) { + maxWidth = MAXIMUM_SIZE; + } + if (maxHeight > MAXIMUM_SIZE) { + maxHeight = MAXIMUM_SIZE; + } + + // Resizes all images + for (final File f : dir.listFiles(imageFilter)) { + BufferedImage inputImage = ImageIO.read(f); + + // creates output image + BufferedImage outputImage = new BufferedImage(maxWidth, + maxHeight, inputImage.getType()); + + // draws input image to the top left corner + Graphics2D g2d = outputImage.createGraphics(); + //TODO The following two lines should be unnecessary + //g2d.setColor(Config.imageBackground); + //g2d.fillRect(0,0, maxWidth, maxHeight); + g2d.drawImage(inputImage, 0, 0, inputImage.getWidth(), inputImage.getHeight(), null); + g2d.dispose(); + + // overwrites image + ImageIO.write(outputImage, "png", f); + } + } + } + + private void resizeImagesWithZoom(String imagesPath) throws IOException { + File dir = new File(imagesPath); + + int[] maxHeightAfterIndex = new int[dir.listFiles(imageFilter).length]; + int[] maxWidthAfterIndex = new int[dir.listFiles(imageFilter).length]; + + if (dir.isDirectory()) { + + Path directoryPath = Paths.get(new File(imagesPath).toURI()); + DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath, "*.png"); + + List<File> filesList = new ArrayList<>(); + for (Path path : stream) { + filesList.add(path.toFile()); + } + File[] files = new File[filesList.size()]; + filesList.toArray(files); + + JCodecPNGtoMP4.sortByNumber(files); + List<Object> list = Arrays.asList(files); + Collections.reverse(list); + list.toArray(files); + + // reads input images and determines maximum required size after that particular image + int currentMaxHeight = 0; + int currentMaxWidth = 0; + int counter = files.length - 1; + for (File f : files) { + BufferedImage inputImage = ImageIO.read(f); + if (currentMaxHeight < inputImage.getHeight()) { + currentMaxHeight = inputImage.getHeight(); + } + if (currentMaxWidth < inputImage.getWidth()) { + currentMaxWidth = inputImage.getWidth(); + } + maxHeightAfterIndex[counter] = currentMaxHeight; + maxWidthAfterIndex[counter] = currentMaxWidth; + counter--; + } + + // Resizes all images + counter = files.length - 1; + for (final File f : files) { + BufferedImage inputImage = ImageIO.read(f); + + // creates output image + BufferedImage outputImage = new BufferedImage(maxWidthAfterIndex[0], // Total maximum W/H + maxHeightAfterIndex[0], inputImage.getType()); + + // draws input image to the top left corner + Graphics2D g2d = outputImage.createGraphics(); + //TODO The following two lines should be unnecessary + //g2d.setColor(Config.imageBackground); + //g2d.fillRect(0,0, maxWidthAfterIndex[0], maxHeightAfterIndex[0]); + + int newHeight = 0; + int newWidth = 0; + + // Width expansion to border if height fits accordingly + double expansionByWidth = (double) maxWidthAfterIndex[0] / maxWidthAfterIndex[counter]; + if (maxHeightAfterIndex[counter] * expansionByWidth <= maxHeightAfterIndex[0]) { + newWidth = (int) Math.floor(inputImage.getWidth() * expansionByWidth); + newHeight = (int) Math.floor(inputImage.getHeight() * expansionByWidth); + } else { + // otherwise height expansion to border and width according to ratio + double expansionByHeight = (double) maxHeightAfterIndex[0] / maxHeightAfterIndex[counter]; + newWidth = (int) Math.floor(inputImage.getWidth() * expansionByHeight); + newHeight = (int) Math.floor(inputImage.getHeight() * expansionByHeight); + } + + g2d.drawImage(inputImage, 0, 0, newWidth, newHeight, null); + g2d.dispose(); + + // overwrites image + ImageIO.write(outputImage, "png", f); + + counter--; + } + } + } +} -- GitLab