Objetivo: aplicar filtros (blanco y negro, blur, contraste) a una carpeta de imágenes en paralelo y comparar tiempos entre ejecución secuencial, pool fijo e hilos virtuales.
out/ con copias filtradas.import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class ProcesarImagenes {
public static void main(String[] args) throws Exception {
Path in = Paths.get("imagenes-in");
Path out = Paths.get("out");
Files.createDirectories(out);
List<Path> archivos = listarImagenes(in);
if (archivos.isEmpty()) {
System.out.println("No hay archivos en " + in.toAbsolutePath());
return;
}
System.out.println("Archivos a procesar: " + archivos.size());
medir("Secuencial", () -> procesarSecuencial(archivos, out));
medir("Pool fijo", () -> procesarConPoolFijo(archivos, out, Runtime.getRuntime().availableProcessors()));
medir("Hilos virtuales", () -> procesarConVirtuales(archivos, out));
}
@FunctionalInterface
interface Task { void run() throws Exception; }
private static void medir(String etiqueta, Task tarea) throws Exception {
long ini = System.nanoTime();
tarea.run();
long fin = System.nanoTime();
System.out.println(etiqueta + " tardo: " + TimeUnit.NANOSECONDS.toMillis(fin - ini) + " ms");
}
private static List<Path> listarImagenes(Path in) throws IOException {
List<Path> res = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(in, "*.{png,jpg,jpeg}")) {
for (Path p : stream) res.add(p);
}
return res;
}
private static void procesarSecuencial(List<Path> archivos, Path out) {
for (Path p : archivos) {
try {
procesarArchivo(p, out, "seq");
} catch (IOException e) {
System.err.println("Error en " + p + ": " + e.getMessage());
}
}
}
private static void procesarConPoolFijo(List<Path> archivos, Path out, int hilos) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(hilos);
try {
List<Callable<Path>> tareas = new ArrayList<>();
for (Path p : archivos) {
tareas.add(() -> procesarArchivo(p, out, "fixed"));
}
List<Future<Path>> resultados = pool.invokeAll(tareas);
for (Future<Path> f : resultados) {
try { f.get(); } catch (Exception e) { System.err.println("Fallo: " + e.getMessage()); }
}
} finally {
pool.shutdown();
pool.awaitTermination(1, TimeUnit.MINUTES);
}
}
private static void procesarConVirtuales(List<Path> archivos, Path out) throws Exception {
try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
List<Callable<Path>> tareas = new ArrayList<>();
for (Path p : archivos) {
tareas.add(() -> procesarArchivo(p, out, "vt"));
}
List<Future<Path>> resultados = pool.invokeAll(tareas);
for (Future<Path> f : resultados) {
try { f.get(); } catch (Exception e) { System.err.println("Fallo: " + e.getMessage()); }
}
}
}
private static Path procesarArchivo(Path origen, Path out, String prefijo) throws IOException {
BufferedImage img = ImageIO.read(origen.toFile());
BufferedImage bn = filtroBlancoNegro(img);
BufferedImage blur = filtroBlur(bn);
BufferedImage contraste = filtroContraste(blur, 1.2);
Path destino = out.resolve(prefijo + "-" + origen.getFileName().toString());
ImageIO.write(contraste, "png", destino.toFile());
return destino;
}
private static BufferedImage filtroBlancoNegro(BufferedImage src) {
BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < src.getHeight(); y++) {
for (int x = 0; x < src.getWidth(); x++) {
Color c = new Color(src.getRGB(x, y));
int g = (int)(0.299 * c.getRed() + 0.587 * c.getGreen() + 0.114 * c.getBlue());
dest.setRGB(x, y, new Color(g, g, g).getRGB());
}
}
return dest;
}
private static BufferedImage filtroBlur(BufferedImage src) {
int w = src.getWidth(), h = src.getHeight();
BufferedImage dest = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int[] kernel = {1,1,1,1,1,1,1,1,1};
int kSum = 9;
for (int y = 1; y < h - 1; y++) {
for (int x = 1; x < w - 1; x++) {
int r = 0, g = 0, b = 0, idx = 0;
for (int ky = -1; ky <= 1; ky++) {
for (int kx = -1; kx <= 1; kx++) {
Color c = new Color(src.getRGB(x + kx, y + ky));
int k = kernel[idx++];
r += c.getRed() * k;
g += c.getGreen() * k;
b += c.getBlue() * k;
}
}
dest.setRGB(x, y, new Color(r / kSum, g / kSum, b / kSum).getRGB());
}
}
return dest;
}
private static BufferedImage filtroContraste(BufferedImage src, double factor) {
BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < src.getHeight(); y++) {
for (int x = 0; x < src.getWidth(); x++) {
Color c = new Color(src.getRGB(x, y));
int r = ajustar(c.getRed(), factor);
int g = ajustar(c.getGreen(), factor);
int b = ajustar(c.getBlue(), factor);
dest.setRGB(x, y, new Color(r, g, b).getRGB());
}
}
return dest;
}
private static int ajustar(int valor, double factor) {
int v = (int)((valor - 128) * factor + 128);
return Math.max(0, Math.min(255, v));
}
}
imagenes-in/ (PNG/JPG).out/ con tres prefijos: seq-, fixed-, vt-, midiendo tiempo en consola.