-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathImageProcessor.java
133 lines (121 loc) · 5.53 KB
/
ImageProcessor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import java.awt.Color;
import java.awt.image.BufferedImage;
import javax.swing.SwingUtilities;
public class ImageProcessor {
private final BufferedImage image;
private final int squareSize;
/**
* Constructs an ImageProcessor with the given image and square size
* @param image The BufferedImage to process
* @param squareSize The side length of the averaging square
*/
public ImageProcessor(BufferedImage image, int squareSize) {
this.image = image;
this.squareSize = squareSize;
}
/**
* Processes single block of the image, calculating the average color by
* setting the entire block to average color
* @param xStart The starting x-coordinate of the block
* @param yStart The starting y-coordinate of the block
*/
private void processBlock(int xStart, int yStart) {
int width = image.getWidth();
int height = image.getHeight();
int xEnd = Math.min(xStart + squareSize, width);
int yEnd = Math.min(yStart + squareSize, height);
long sumRed = 0, sumGreen = 0, sumBlue = 0;
int countOrigPixels = 0;
// Accumulate RGB values within the block
for (int y = yStart; y < yEnd; y++) {
for (int x = xStart; x < xEnd; x++) {
int rgb = image.getRGB(x, y);
Color color = new Color(rgb);
sumRed += color.getRed();
sumGreen += color.getGreen();
sumBlue += color.getBlue();
countOrigPixels++; // Count the number of pixels in the block
}
}
// Calculate average RGB values
int avgRed = (int) (sumRed / countOrigPixels);
int avgGreen = (int) (sumGreen / countOrigPixels);
int avgBlue = (int) (sumBlue / countOrigPixels);
Color avgColor = new Color(avgRed, avgGreen, avgBlue);
// Set the entire block to the average color
for (int y = yStart; y < yEnd; y++) {
for (int x = xStart; x < xEnd; x++) {
image.setRGB(x, y, avgColor.getRGB()); // Set the RGB value of the pixel
}
}
}
/**
* Processes the image in a single-thread, averaging each block sequentially
* @param repaintCallback callback to repaint the image in the GUI
*/
public void processImageSingleThreaded(Runnable repaintCallback) {
int width = image.getWidth();
int height = image.getHeight();
// Iterate the image while squareSize increments
for (int y = 0; y < height; y += squareSize) {
for (int x = 0; x < width; x += squareSize) {
processBlock(x, y);
// Schedule a repaint on the Event Dispatch Thread (EDT)
SwingUtilities.invokeLater(repaintCallback);
try {
Thread.sleep(10); // Delay to visualize
} catch (InterruptedException e) {
System.err.println("Error: Thread interrupted.");
Thread.currentThread().interrupt(); // Restore interrupt
}
}
}
// Final repaint to ensure all changes are displayed
SwingUtilities.invokeLater(repaintCallback);
}
/**
* Processes the image in multiple-threads, dividing the image into segments
* based on the number of available CPU cores
* @param repaintCallback callback to repaint the image in the GUI
*/
public void processImageMultiThreaded(Runnable repaintCallback) {
int numThreads = Runtime.getRuntime().availableProcessors(); // Number of available CPU cores
Thread[] threads = new Thread[numThreads]; // Array to store threads
int height = image.getHeight();
int segmentHeight = height / numThreads;
// Create and start threads for each segment
for (int i = 0; i < numThreads; i++) {
final int threadIndex = i;
threads[i] = new Thread(() -> {
int startY = threadIndex * segmentHeight; // Start of the segment
int endY = (threadIndex == numThreads - 1) ? height : startY + segmentHeight; // Last thread handles the remainder
// Iterate the image while squareSize increments within the segment assigned to the thread
for (int y = startY; y < endY; y += squareSize) {
for (int x = 0; x < image.getWidth(); x += squareSize) {
processBlock(x, y);
SwingUtilities.invokeLater(repaintCallback); // Schedule a repaint on the Event Dispatch Thread (EDT)
try {
Thread.sleep(10); // Delay to visualize
} catch (InterruptedException e) {
System.err.println("Error: Thread interrupted.");
Thread.currentThread().interrupt(); // Restore interrupt
}
}
}
});
threads[i].start();
}
// Waiting for all threads to complete
for (Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
System.err.println("Error: Thread interrupted.");
Thread.currentThread().interrupt(); // Restore interrupt
}
}
// Final repaint after all threads have finished
SwingUtilities.invokeLater(repaintCallback);
}
}
// Source for Event Dispatch Thread (EDT): https://github.com/mgarin/weblaf/wiki/Event-Dispatch-Thread