ImageJ plugin that draws a white frame inside of a ROI - java

I am trying to create a plugin on ImageJ that creates a 10 pixels wide frame inside of the ROI. I am having trouble doing this, the code I have so far simply fills in the the entire space start 10 pixels in the ROI. The code I have is below.
import ij.*;
import ij.plugin.filter.PlugInFilter;
import ij.process.*;
import java.awt.*;
public class Frame implements PlugInFilter {
public int setup(String arg, ImagePlus imp) {
if (arg.equals("about"))
{showAbout(); return DONE;}
return DOES_8G+DOES_STACKS+SUPPORTS_MASKING;
}
public void run(ImageProcessor ip) {
byte[] pixels = (byte[])ip.getPixels();
int width = ip.getWidth();
Rectangle r = ip.getRoi();
int offset, i;
for (int y=r.y+10; y<(r.y+r.height)-10; y++) {
offset = y*width;
for (int x=r.x+10; x<(r.x+r.width)-10; x++) {
i = offset + x ;
pixels[i] = (byte)(255);
}
}
}

Why do you try it the hard (i.e. low level API) way?
Using ImageJ's command recorder (Plugins > Macros > Record... and setting "Record:" to Java), you can quickly get the required Java code after
creating a selection,
adding it to the ROI manager,
running Edit > Selection > Enlarge...,
adding the new ROI to the manager,
combining the two ROIs using XOR, and
filling the resulting selection with the foreground color.
The code would look like this:
import ij.IJ;
import ij.ImagePlus;
import ij.plugin.frame.RoiManager;
// [...]
RoiManager rm = RoiManager.getInstance();
if (rm==null) rm = new RoiManager();
rm.runCommand("Deselect");
if (rm.getCount() > 0) rm.runCommand("Delete");
ImagePlus imp = IJ.getImage();
// imp.setRoi(30, 50, 150, 100);
rm.addRoi(imp.getRoi());
IJ.run(imp, "Enlarge...", "enlarge=-10");
rm.addRoi(imp.getRoi());
rm.setSelectedIndexes(new int[]{0,1});
rm.runCommand("XOR");
IJ.run(imp, "Fill", "slice");
imp.show();

Related

Creating diagram for an extremely immense tree using Java

The problem is simple. I want to create a vertical tree of values, where as you descend down the levels, the amount of values gets exponentially larger. Let's say the 1st level has 1 numerical value, the next has 10, then the next has 100, then the next has 1000, and so on. The first level is connected to the 2nd level with the use of lines, and the 2nd to the 3rd, and so on, much like a game tree. These values are also evenly spaced, so let's say you have a JPanel which is 500x500. At a y of 100, you have 4 values, and so to evenly space them out, you would have a value at 100, one at 200, and so on.
I've tried incorporating drawString, and connecting them with the drawLine method, and placing this so called diagram on a JPanel. That is actually quite simple, and it works if you only have as many as about 50~ values in a singular level. However, when you only have a 1600x900 screen, you can't fit 100 values (on the x axis, which is 1600) without having a big jumbled up mess.
I was thinking there could be 2 possible solutions for this.
One is that the JPanel is not limited to a set resolution (a.k.a the size of your screen) and is scrollable. If it was 10000 x 900 then making this gigantic tree diagram would actually be quite simple, and I could easily fit the 100 values with enough space between them for the values to actually be discernable. However, as far as I know, it's not possible.
The second solution is that I write these values into a file, but I'm not sure how to go about this.
Does anyone know, theoretically speaking, what could be the simplest solution for properly displaying a large tree diagram filled with hundreds of values in a single level?
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay particular attention to the Performing Custom Painting section.
It turns out it's possible to create one 10000 x 900 drawing JPanel. Adjust the JScrollPane preferred size to the size you want to display. The height should be at least 950 pixels to allow room for the horizontal scroll bar.
I created a checkerboard pattern so you can see that the drawing JPanel does scroll.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LargeDrawingJPanel implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LargeDrawingJPanel());
}
#Override
public void run() {
JFrame frame = new JFrame("Large Drawing JPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel drawingPanel = new DrawingPanel();
JScrollPane scrollPane = new JScrollPane(drawingPanel);
scrollPane.setPreferredSize(new Dimension(1400, 950));
frame.add(scrollPane, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(10000, 900));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Color[] colors = { Color.RED, Color.BLACK };
int colorIndex = 0;
int x = 0;
int y = 0;
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 9; j++) {
g2d.setColor(colors[colorIndex]);
colorIndex = (colorIndex == 0) ? 1 : 0;
g2d.fillRect(x, y, 100, 100);
y += 100;
}
x += 100;
y = 0;
}
}
}
}

Robot.mouseMove not moving to specified location properly

Whenever I run a mouseMove command for a robot, the mouse doesn't always go to the same location. For example, I have the following code:
import java.awt.Robot;
import java.util.concurrent.TimeUnit;
public class MainBot {
public static void main(String[] args){
try {
Robot screenWin = new Robot();
TimeUnit.SECONDS.sleep(2);
screenWin.mouseMove(100, 300);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The code usually makes the mouse end up at the X:
First, I hit run (I am using eclipse) and move my mouse to a location (before the 2 second timer is up). Then the 2 second delay finishes and the mouse moves and then the script ends. The problem is, the mouse never seems to go to the same exact place twice. For example, the mouse should go to (100, 300) but it goes to something that looks like (0, 300) most of the time. Other times, however, if I move the mouse at the beginning to where it should roughly be, then it goes to the right spot.
I am getting where the mouse should be using Paint to get the pixel location of a screenshot but I don't think it is that because the location keeps changing.
Is there anything I'm missing how the coordinates for mouseMove work?
Edit: Basically, I hit start with that program, then I move the mouse to a new position (so there is a different initial position before the mouseMove function) and then mouseMove executes. Each time I do this, the mouse goes to a different location.
There's an open bug on OpenJDK, so this could be related:
https://bugs.openjdk.java.net/browse/JDK-8196030?jql=project%20in%20(JDK)%20AND%20component%20in%20(client-libs)%20AND%20Subcomponent%20in%20(java.awt)
The bug details that a problem may have been introduced in Windows 10 Fall Creators update, related to screen scaling and a mouse_move function.
In the meantime, you could try to set your screen scale to 100% instead of 125% and see if it helps.
I found a solution, you just have to move the mouse to the coordinate (0,0) then you can move it to the place you want.
I wrote a class to do proper cursor positioning.
This works under windows 10 scalings too.
Use the MoveMouseControlled(double, double) function to move the cursor to a specified position. It uses a [0,1] coordinate system. The (0,0) Point is the upper left corner of the screen.
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
public class MouseCorrectRobot extends Robot
{
final Dimension ScreenSize;// Primary Screen Size
public MouseCorrectRobot() throws AWTException
{
super();
ScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
}
private static double getTav(Point a, Point b)
{
return Math.sqrt((double) ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
}
public void MoveMouseControlled(double xbe, double ybe)// Position of the cursor in [0,1] ranges. (0,0) is the upper left corner
{
int xbepix = (int) (ScreenSize.width * xbe);
int ybepix = (int) (ScreenSize.height * ybe);
int x = xbepix;
int y = ybepix;
Point mert = MouseInfo.getPointerInfo().getLocation();
Point ElozoInitPont = new Point(0, 0);
int UgyanAztMeri = 0;
final int UgyanAZtMeriLimit = 30;
int i = 0;
final int LepesLimit = 20000;
while ((mert.x != xbepix || mert.y != ybepix) && i < LepesLimit && UgyanAztMeri < UgyanAZtMeriLimit)
{
++i;
if (mert.x < xbepix)
++x;
else
--x;
if (mert.y < ybepix)
++y;
else
--y;
mouseMove(x, y);
mert = MouseInfo.getPointerInfo().getLocation();
if (getTav(ElozoInitPont, mert) < 5)
++UgyanAztMeri;
else
{
UgyanAztMeri = 0;
ElozoInitPont.x = mert.x;
ElozoInitPont.y = mert.y;
}
}
}
}
I just had a similar problem, to solve it I’ve just done a loop :
Test position
Move
Test position
if not OK move again
And it always works in less than 2 loops
Point pd = new Point(X,Y); // X,Y where mouse must go
int n = 0;
while ((!pd.equals(MouseInfo.getPointerInfo().getLocation())) && (++n <= 5))
{
r.mouseMove(pd.x, pd.y);
}
It works well (correct location) in Full Screen mode with zoom=100%. press F-11 in chrome to full screen page.

Multi-colored JLabel

I would like a different way to create a multi-colored JLabel.
(Multi-colored = parts of the text in different foreground-colors)
The only solution I found so far (and which I currently use), is setting the text in html. But I'm having problems with that...
When the LayoutManager decides that the JLabel should be narrowed, with a plain-text in a JLabel, the text gets kind of cropped, and "..." is added.
(e.g.: "My Long Text" -> becomes: "My Long T...")
With html inside a JLabel, the text is wrapped somewhere on a space-character, leaving the rest of outside the drawable area, and invisible as the JLabel's height is unchanged.
(e.g.: "My Long Text" -> becomes: "My Long")
In my case the JLabel is rendered in a JTable, which gets resized by the user, not to mention in different screen resolutions.
I tried adding a "nowrap" attribute or a ""-tag to the html, but it looks like this is ignored.
Leaving me -I think- with one solution: painting the label myself.
Or not?
Any suggestions?
Examples?
Thank you.
Here's a very simple example:
Try to resize this panel horizontally, and see what happens with the text inside both JLabel's...
(there's no indication for the user, that the text of the second JLabel is not the complete content)
-> In the example, the JLabel's height changes, but when rendered inside the framework's JTable, the height of the rows doesn't change and I don't want it to change. Without the use of HTML it doesn't change the height either...
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MultiJLabel
extends JFrame
{
public MultiJLabel()
{
super("Multi-colored JLabel test");
JPanel pnl = new JPanel();
pnl.setLayout(new BorderLayout());
pnl.add(new JLabel("This is a test of My Long Text"), BorderLayout.NORTH);
pnl.add(new JLabel("<html>This is a test of <font color='#ffbebe'>My Long Text</font></html>"), BorderLayout.SOUTH);
this.getContentPane().add(pnl);
this.pack();
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
}
public static void main(String[] args)
{
new MultiJLabel();
}
}
Here's a picture of the original problem, where our users are not aware that the client's Order Number is not what the grid is showing, because this column has HTML-formatted text to show multi-colors.
Thank you all for your comments, but I was impatient and created my own JLabel.
I know it may be a poor programmed version, but it works for me...
You can test it by altering the above example with:
JMultiColorLabel lbl = new JMultiColorLabel("This is a test of My Long Text");
lbl.setColors(new int[]{10,15}, new Color[]{Color.RED,Color.BLUE});
lbl.setPreferredSize(new Dimension(200,20));
pnl.add(lbl, BorderLayout.SOUTH);
And use this class:
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.util.HashMap;
import javax.swing.JLabel;
public class JMultiColorLabel
extends JLabel
{
private static final String STRING_OVERFLOW = "...";
private HashMap<Integer, Color> extraColors = new HashMap<Integer, Color>();
public JMultiColorLabel(String text)
{
super(text);
}
public void setColors(int[] indices, Color[] colors)
{
for (int i = 0; i < indices.length; i++)
this.extraColors.put(indices[i], colors[i]);
}
protected void paintComponent(Graphics g)
{
// Get text-contents of Label
String text = this.getText();
// No text in the JLabel? -> No risk: super
if (text == null || text.length() == 0)
{
super.paintComponent(g);
return;
}
// Content Array of characters to paint
char[] chars = text.toCharArray();
// Draw nice and smooth
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// Draw background
if (this.isOpaque())
{
g2d.setColor(this.getBackground());
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
}
// FontMetrics to calculate widths and height
FontMetrics fm = g2d.getFontMetrics();
// Available space
Insets ins = this.getInsets();
int maxSpace = this.getWidth()-(ins.left+ins.right);
boolean overflow = (fm.stringWidth(text) > maxSpace);
// Starting offset
int offset = ins.left+1;
// The start Color is the default
g2d.setColor(this.getForeground());
// Loop over characters
for (int i = 0; i < chars.length; i++)
{
// Switch Color?
if (this.extraColors.containsKey(i))
g2d.setColor(this.extraColors.get(i));
// Check if we still have enough room for this character
if (overflow && offset >= (maxSpace-fm.stringWidth(STRING_OVERFLOW)))
{ // Draw overflow and stop painting
g2d.drawString(STRING_OVERFLOW, offset, (fm.getHeight()+ins.top));
return;
}
else // We have the space -> Draw the character
g2d.drawString(String.valueOf(chars[i]), offset, (fm.getHeight()+ins.top));
// Move cursor to the next horizontal position
offset += fm.charWidth(chars[i]);
}
}
}
To prevent line wrapping when using html-text in JLabels, wrap the text in nobr (no-break) tags:
new JLabel("<html><nobr>This is a test of <font color='#ffbebe'>My Long Text</font></nobr></html>")
When using the nobr-tags, the line will not be wrapped, but it won't be truncated as well. So, there won't be any ellipsis (...) at the end of the shown text, but it will just cut off.
The missing ... might actually be of advantage in a table as there is no additional width lost by the ellipsis, and thus more content shown. But to the user it might be less obvious that there is more content without them.

Visual Custom Grid

I am new to Swing in Java and am trying to make a visual grid. I have some simple questions as to how to do the following. Here are some characteristics of the grid:
For each square of the grid, I should be having an equivalent index(i,j) using which I can identify the square and place a text.
Edit - (i,j) is the row/column value - the index of the square in the grid.
The diagonals of the squares should be drawn and each of the four divisions of the square should be having a different color.
Any suggestions as to how to do so.
Given row and column you will need to know the number of columns per row. With this information you can simply do (row * columns) + column which will return the index of the square.
For example, with 8 columns per row, a request for
row = 0, column = 4, will return 4
row = 1, column = 4, will return 12
row = 0, column = 0, will return 0
Rendering the sections of the square is more complex and can be achieved in at least two ways. You could use a Polygon which generates a triangle for each section or Shape and simply use Graphics2D#fill to fill it.
This will come down to how you physically render each square...
Take a look at 2D Graphics for more details and this for an example of both.
A Shape would be much easier to rotate and position and would only require you to have a single instance (or a single instance for each square based on your needs), where as you would require at least 4 Polygons or do some fun rotation...
Updated with simple example
All done with rotating triangles...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DividedSquare {
public static void main(String[] args) {
new DividedSquare();
}
public DividedSquare() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private TriangleShape baseTriangle;
private Color[] colors;
public TestPane() {
colors = new Color[]{Color.RED, Color.GREEN, Color.BLUE, Color.MAGENTA};
}
#Override
public void invalidate() {
super.invalidate();
baseTriangle = new TriangleShape(
new Point(0, 0),
new Point(getWidth(), 0),
new Point(getWidth() / 2, getHeight() / 2));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
String text[] = new String[]{
"123.123",
"456.789",
"012.315",
"678.921"
};
FontMetrics fm = g2d.getFontMetrics();
double angel = 0;
for (int index = 0; index < 4; index++) {
g2d.setColor(colors[index]);
Path2D rotated = rotate(baseTriangle, angel);
g2d.fill(rotated);
Rectangle bounds = rotated.getBounds();
int x = bounds.x + ((bounds.width - fm.stringWidth(text[0])) / 2);
int y = bounds.y + (((bounds.height - fm.getHeight()) / 2) + fm.getAscent());
g2d.setColor(Color.WHITE);
g2d.drawString(text[index], x, y);
angel += 90;
}
g2d.setColor(Color.BLACK);
g2d.drawLine(0, 0, getWidth(), getHeight());
g2d.drawLine(getWidth(), 0, 0, getHeight());
g2d.dispose();
}
public Path2D rotate(TriangleShape shape, double angel) {
Rectangle bounds = shape.getBounds();
int x = bounds.width / 2;
int y = bounds.width / 2;
return new Path2D.Float(shape, AffineTransform.getRotateInstance(
Math.toRadians(angel),
x,
y));
}
}
public class TriangleShape extends Path2D.Double {
public TriangleShape(Point2D... points) {
moveTo(points[0].getX(), points[0].getY());
lineTo(points[1].getX(), points[1].getY());
lineTo(points[2].getX(), points[2].getY());
closePath();
}
}
}
Yes, see Making a robust, resizable Chess GUI for a GUI that uses a GridLayout to contain JButton objects. The buttons are held in an 8x8 array (the chessboard), while displayed in a 9 row x 9 column grid layout to accommodate the row (8-1) & column (A-H) identifiers of the board.
Buttons will accept an image that might represent the '4 color diagonal' and text. In the chess board I only use images (the chess pieces).
Edit 1
What is the actual difference between the 4 colored areas (besides the color). E.G. is there supposed to be different functionality depending on which of the diagonal areas the user clicks in?
Each of the 4 colored areas in the square has a value from a table based on some algorithm. The color depends upon the value. Currently, it is just used as an indicator. But the user needs to select an initial square in the grid where I place a mark that it is initial.
And the text. Should it be on top of the '4 color diagonal', beside it, below it..?
I also plan to place a text in each of the region indicating that value. Hence, there is one text field to be placed in each of the four partitions of the square.
Text field or label? A label can show text, but a text field makes it possible to copy the text or change it.
I need a text field because I need to change the text during the course of the algorithm.
By 'I' DYM the program, or the user? The program can change text in a label or text field, but the text field is user editable.
I mean the program
OK - Draw the design and text on an image. Use the image as a button icon. The user can select an initial square by clicking the button. If the text changes, generate a new image and set it to the button.
Edit 2
So do you mean that I have to create an image with 4 colors? Would that not be difficult. I would like to change the colors from the program itself. My algorithm would generate the values and at certain points of the algorithm, I would like to see the visualization.
Not if you generate the image at run-time.
How do I generate an image at run-time?
See the answer to Example images for code and mark-up Q&As The first set of Icons..
...
..As well as the Sprite sheet..
..were generated at run-time in Java code. Each set of images links to the question that includes the code that generated them.
I believe you're describing the SetGridLayout feature in swing. If you want a tutorial on how to set up such a window, you can find it here:
http://docs.oracle.com/javase/tutorial/uiswing/layout/grid.html
After reading your question a second time... I think you plan on drawing a grid...
I'd look into the Draw.java library
Maybe you can try to do this adding a jTable object, this object contains methods that can put values in every value i and j respectively like:
jTable1.setValueAt(Value, i, j);
this will allows you to change the value in every cell.

Determining if an image has a particular colour spectrum

I'm wondering if there are any algorithms out there written in Java currently for determining if an image has a low range of different pixel colours contained within it.
I'm trying to detect placeholder images (that typically consist of high percentages of single colours (typically white and grey pixels) as opposed to full colour photos (that consist of a plethora of multiple colours).
If nothing exists, I'll write something myself (was thinking about sampling an arbitrary pixels in random positions across the image or averaging out all pixel colours contained across the image) and then determining quantities of the different colours I find. There may be a trade off between speed and accuracy depending on the methodology used.
Any advice / pointers / reading material appreciated.
A way to do it would be:
final BufferedImage image = // your image;
final Set<Integer> colours = new HashSet<Integer>();
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
colours.add(image.getRGB(x, y));
}
}
// Check your pixels here. In grayscale images the R equals G equals B
You can also use the Java Advanced Imaging(JAI) since it provides a Histogram class:
package com.datroop.histogram;
import java.awt.image.renderable.ParameterBlock;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import javax.media.jai.Histogram;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.ROI;
import javax.media.jai.RenderedOp;
public class HistogramCreator {
private HistogramCreator() {
}
public static int[] createHistogram(final PlanarImage image) {
// set up the histogram
final int[] bins = { 256 };
final double[] low = { 0.0D };
final double[] high = { 256.0D };
Histogram histogram = new Histogram(bins, low, high);
final ParameterBlock pb = new ParameterBlock();
pb.addSource(image);
pb.add(null);
pb.add(1);
pb.add(1);
final RenderedOp op = JAI.create("histogram", pb);
histogram = (Histogram) op.getProperty("histogram");
// get histogram contents
final int[] local_array = new int[histogram.getNumBins(0)];
for (int i = 0; i < histogram.getNumBins(0); i++) {
local_array[i] = histogram.getBinSize(0, i);
}
return local_array;
}
public static void main(String[] args) {
try {
String filename = "file://localhost/C:/myimage.jpg";
System.setProperty("com.sun.media.jai.disableMediaLib", "true");
// Create the histogram
int[] myHistogram = createHistogram(JAI.create("url", new URL(filename)));
// Check it out here
System.out.println(Arrays.toString(myHistogram));
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
A simple low-overhead approach would be to do a histogram of the Red, Green and Blue component values separately. There would only be 256 colour levels for each so it would be quite efficient - you could build each histogram in an new int[256] array.
Then you just count the number of non-zero values in each of the histograms and check whether they all meet some threshold (say at least 20 different values in each would imply a photograph)
An alternative approach would be to create a HashSet of colour values in the image, and keep adding to the HashSet as you scan the image. Since HashSets hold unique values, it will store only the unqique colours. To avoid the HashSet getting too large, you can bail out when the size of the HashSet hits a pre-determined threshold (maybe 1000 unique colours?) and conclude that you have a photograph.

Categories