I'd like to make my Java program compare the actual screen with a picture (screenshot).
I don't know if it's possible, but I have seen it in Jitbit (a macro recorder) and I would like to implement it myself. (Maybe with that example you understand what I mean).
Thanks
----edit-----
In other words, is it possible to check if an image is showing in? To find and compare that pixels in the screen?
You may try aShot: documentation link
1) aShot can ignore areas you mark with special color.
2) aShot can provide image which display difference between images.
private void compareTowImages(BufferedImage expectedImage, BufferedImage actualImage) {
ImageDiffer imageDiffer = new ImageDiffer();
ImageDiff diff = imageDiffer
.withDiffMarkupPolicy(new PointsMarkupPolicy()
.withDiffColor(Color.YELLOW))
.withIgnoredColor(Color.MAGENTA)
.makeDiff(expectedImage, actualImage);
// areImagesDifferent will be true if images are different, false - images the same
boolean areImagesDifferent = diff.hasDiff();
if (areImagesDifferent) {
// code in case of failure
} else {
// Code in case of success
}
}
To save image with differences:
private void saveImage(BufferedImage image, String imageName) {
// Path where you are going to save image
String outputFilePath = String.format("target/%s.png", imageName);
File outputFile = new File(outputFilePath);
try {
ImageIO.write(image, "png", outputFile);
} catch (IOException e) {
// Some code in case of failure
}
}
You can do this in two steps:
Create a screenshot using awt.Robot
BufferedImage image = new Robot().createScreenCapture(new Rctangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIO.write(image, "png", new File("/screenshot.png"));
Compare the screenshots using something like that: How to check if two images are similar or not using openCV in java?
Have a look at Sikuli project. Their automation engine is based on image comparison.
I guess, internally they are still using OpenCV for calculating image similarity, but there are plenty of OpenCV Java bindings like this, which allow to do so from Java.
Project source code is located here: https://github.com/sikuli/sikuli
Ok then, so I found an answer after a few days.
This method takes the screenshot:
public static void takeScreenshot() {
try {
BufferedImage image = new Robot().createScreenCapture(new Rectangle(490,490,30,30));
/* this two first parameters are the initial X and Y coordinates. And the last ones are the increment of each axis*/
ImageIO.write(image, "png", new File("C:\\Example\\Folder\\capture.png"));
} catch (IOException e) {
e.printStackTrace();
} catch (HeadlessException e) {
e.printStackTrace();
} catch (AWTException e) {
e.printStackTrace();
}
}
And this other one will compare the images
public static String compareImage() throws Exception {
// savedImage is the image we want to look for in the new screenshot.
// Both must have the same width and height
String c1 = "savedImage";
String c2 = "capture";
BufferedInputStream in = new BufferedInputStream(new FileInputStream(c1
+ ".png"));
BufferedInputStream in1 = new BufferedInputStream(new FileInputStream(
c2 + ".png"));
int i, j;
int k = 1;
while (((i = in.read()) != -1) && ((j = in1.read()) != -1)) {
if (i != j) {
k = 0;
break;
}
}
in.close();
in1.close();
if (k == 1) {
System.out.println("Ok...");
return "Ok";
} else {
System.out.println("Fail ...");
return "Fail";
}
}
Related
I trying to create a custom photo gallery which the user can store Images from MediaStore photo gallery,
And my target is that the user can only the store different Image in database, if it is a duplicate Image it will not store .So , Im thinking hey, I can use the filepath as sort of A Id to check for duplicate Images. But evertime I restart the app and repick the same picture the Uri/filepath is not the same espicially the lastpart of the filePath/Uri
My Weird Implementation to Check duplicate:
public void checkAndSetPhotoGalleryData() throws IOException {
RoomDB db = RoomDB.getInstance(this);
PhotoGalleryData photoGalleryData = new PhotoGalleryData();
String stringUri = String.valueOf(imageUri);
String lastPartFile = stringUri.substring(stringUri.lastIndexOf('/')+1);
TextView testThis = findViewById(R.id.testName);
testThis.setText(db_path);
if(photoGarList.isEmpty()) {
try {
InputStream iStream = getContentResolver().openInputStream(imageUri);
byte[] inputData = getBytes(iStream);
photoGalleryData.setKey_Value_Quiz(String.valueOf(lastPartFile));
photoGalleryData.setPhoto(inputData);
db.questionDao().insert(photoGalleryData);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}if(!photoGarList.isEmpty())
{for(int i = 0; i<photoGarList.size();i++){
if (photoGarList.get(i).getKey_Value_Quiz().length() > 0 && !photoGarList.isEmpty() && photoGarList.get(i).getKey_Value_Quiz().equals(String.valueOf(lastPartFile))) {
showLongToast("Are Identical");
}
if (photoGarList.get(i).getKey_Value_Quiz().length() > 0 && !photoGarList.isEmpty() && !photoGarList.get(i).getKey_Value_Quiz().equals(String.valueOf(lastPartFile)) && i == photoGarList.size()-1) {
try {
InputStream iStream = getContentResolver().openInputStream(imageUri);
byte[] inputData = getBytes(iStream);
photoGalleryData.setKey_Value_Quiz(String.valueOf(lastPartFile));
photoGalleryData.setPhoto(inputData);
db.questionDao().insert(photoGalleryData);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
lastPart of filePath:
///both of this are the same picture:
KeyValue:924270434
KeyValue:239256090
I guess that for security reason they change the last part every time, So I want to know is there any constant factor of a picture except for byte[] or Bitmap(Size is to Huge to store at once and this is not the case).
You could use simple pixel by pixel comparison of the images.
Try to use ImageMagic:
ImageMagic
Or you could try Java OpenCV: How to compare two images using Java OpenCV library
i have this code below, but it is not efficient at all, it is very very slow and more pictures i have to compare more long time it takes.
For example i have 500 pictures, each process lasts 2 minutes, 500 x 2 min =1000 min !
the specificity is as soon as there is picture same as compared, move it to another folder. then retrieve the rest files to compare i++
any idea ?
public static void main(String[] args) throws IOException {
String PicturesFolderPath=null;
String removedFolderPath=null;
String pictureExtension=null;
if(args.length>0) {
PicturesFolderPath=args[0];
removedFolderPath=args[1];
pictureExtension=args[2];
}
if(StringUtils.isBlank(pictureExtension)) {
pictureExtension="jpg";
}
if(StringUtils.isBlank(removedFolderPath)) {
removedFolderPath=Paths.get(".").toAbsolutePath().normalize().toString()+"/removed";
}
if(StringUtils.isBlank(PicturesFolderPath)) {
PicturesFolderPath=Paths.get(".").toAbsolutePath().normalize().toString();
}
System.out.println("path to find pictures folder "+PicturesFolderPath);
System.out.println("path to find removed pictures folder "+removedFolderPath);
Collection<File> fileList = FileUtils.listFiles(new File(PicturesFolderPath), new String[] { pictureExtension }, false);
System.out.println("there is "+fileList.size()+" files founded with extention "+pictureExtension);
Iterator<File> fileIterator=fileList.iterator();
//Iterator<File> loopFileIterator=fileList.iterator();
File dest=new File(removedFolderPath);
while(fileIterator.hasNext()) {
File file=fileIterator.next();
System.out.println("process image :"+file.getName());
//each new iteration we retrieve the files staying
Collection<File> list = FileUtils.listFiles(new File(PicturesFolderPath), new String[] { pictureExtension }, false);
for(File f:list) {
if(compareImage(file,f) && !file.getName().equals(f.getName()) ) {
String filename=file.getName();
System.out.println("file :"+file.getName() +" equal to "+f.getName()+" and will be moved on removed folder");
File existFile=new File(removedFolderPath+"/"+file.getName());
if(existFile.exists()) {
existFile.delete();
}
FileUtils.moveFileToDirectory(file, dest, false);
fileIterator.remove();
System.out.println("file :"+filename+" removed");
break;
}
}
}
}
// This API will compare two image file //
// return true if both image files are equal else return false//**
public static boolean compareImage(File fileA, File fileB) {
try {
// take buffer data from botm image files //
BufferedImage biA = ImageIO.read(fileA);
DataBuffer dbA = biA.getData().getDataBuffer();
int sizeA = dbA.getSize();
BufferedImage biB = ImageIO.read(fileB);
DataBuffer dbB = biB.getData().getDataBuffer();
int sizeB = dbB.getSize();
// compare data-buffer objects //
if(sizeA == sizeB) {
for(int i=0; i<sizeA; i++) {
if(dbA.getElem(i) != dbB.getElem(i)) {
return false;
}
}
return true;
}
else {
return false;
}
}
catch (Exception e) {
e.printStackTrace();
return false;
}
}
The already mentioned answer should help you a bit, as considering the width and height of a picture should exclude more candidate pairs quickly.
However, you still have a big problem: For every new file, you read all old files. The number of comparisons grows quadratically and with doing ImageIO.read for every step, it simply must be slow.
You need some fingerprints, which can be compared very fast. You can't use fingerprinting over the whole file content as its infested by the metadata, but you can fingerprint the image data alone.
Just iterate over the image data of a file (like you do), and compute e.g., MD5 hash of it. Store it e.g., as a String in HashSet and you'll get a very fast lookup.
Some untested code
For every image file you want to compare, you compute (using Guava's hashing)
HashCode imageFingerprint(File file) {
Hasher hasher = Hashing.md5().newHasher();
BufferedImage image = ImageIO.read(file);
DataBuffer buffer = image.getData().getDataBuffer();
int size = buffer.getSize();
for(int i=0; i<size; i++) {
hasher.putInt(buffer.getElem(i));
}
return hasher.hash();
}
The computation works with the image data only, just like compareImage in the question, so the metadata get ignored.
Instead of searching for a duplicate in a directory, you compute the fingerprints of all its files and store them in a HashSet<HashCode>. For a new file, you compute its fingerprint and look it up in the set.
This is what I have so far, and I am having trouble downloading 1-100 comics starting at https://xkcd.com/1/ and I know I am supposed to be going to the source code for the website. However, I cant seem to figure out how to get all the first 100 comics into my designated file I set it to save to. For example, I want https://xkcd.com/1/(view-source:https://xkcd.com/1/), https://xkcd.com/2/(view-source:https://xkcd.com/2/), and all the way up to comic 100. I know the img src is at line 50, but once again I don't know how to approach it.
public static void main(String[] args) {
URL imgURL = null;
for (int web = 1; web <= 100; web++) {
try {
imgURL = new URL("https://imgs.xkcd.com/comics/barrel_cropped_(1).jpg");
InputStream stream = imgURL.openStream();
Files.copy(stream, Paths.get("file/WebComics" + web + ".png"));
System.out.println("Done!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error!");
}
}
}
}
Add jsoup library jar to your project, and then try this:
static void do_page(int id) throws IOException {
Document doc = Jsoup.connect("https://xkcd.com/" + id).get();
Elements imgs = doc.select("#comic img");
for (Element e: imgs) {
System.out.println(e.attr("src"));
}
}
Then call the do_page function in a loop:
for (int i = 1; i <= 100; i++) {
do_page(i);
}
Now, instead of printing it, you can use JSoup again to probably download the images like you see fit.
I'm trying to use ImageJ2 directly from Java to create a binarised image coming from an input image.
A somewhat working version of my code looks like this:
final File file = new File("input.png");
try {
DefaultDataTypeService dataTypeService = new DefaultDataTypeService();
Dataset dataset = imageJ.dataset().open(file.getAbsolutePath());
Img inputImg = dataset.getImgPlus();
PluginInfo pluginInfo = imageJ.plugin().getPlugin(Binarize.class);
Binarize binarizeOp = (Binarize) pluginInfo.createInstance();
binarizeOp.setContext(imageJ.getContext());
binarizeOp.setChangeInput(true);
binarizeOp.setFillMaskBackground(true);
binarizeOp.setFillMaskForeground(true);
binarizeOp.setInputData(dataset);
binarizeOp.setInputMask(null);
binarizeOp.setMaskColor(Binarize.WHITE);
binarizeOp.setMaskPixels(Binarize.INSIDE);
binarizeOp.setThresholdEachPlane(false);
binarizeOp.setDefaultThresholdMethod();
binarizeOp.run();
dataset.rgbChange();
DefaultDatasetService defaultDatasetService = new DefaultDatasetService();
Img outputImg = dataset.getImgPlus();
outputImg = outputImg.factory().imgFactory(new UnsignedByteType()).create(outputImg,new UnsignedByteType());
Dataset outputDataset = defaultDatasetService.create(outputImg);
imageJ.dataset().save(outputDataset,"input_binary.png");
} catch (IOException e) {
e.printStackTrace();
} catch (InstantiableException e) {
e.printStackTrace();
} catch (IncompatibleTypeException e) {
e.printStackTrace();
}
Running this code I have the problem that "input_binary.png" will be completely black, a behaviour I can reproduce using the ImageJ client application.
What I need to do in the client is to change the image type from "8-bit Color" to "RGB-Color". But I can not figure out how to reproduce that in Java using the current version of the net.imagej library.
I know that it would be possible using the 1.x library but I would like to to it using the 2.x.
Any help would be greatly appreciated.
You're getting black images because of this:
outputImg = outputImg.factory().imgFactory(new UnsignedByteType()).create(outputImg,new UnsignedByteType());
Which is just copying the dimensionality of your source image, not its values.
A few other key points:
It's best practice to have your Contextual objects (e.g. Services) derived from the Context instead of manually constructed.
The Binarize command has a Dataset output so it's not necessary to go Dataset > ImgPlus > Dataset
If you do want to write the dataset out you need to convert from the BitType output by Binarize to one that's supported.
See below for an example of running Binarize, getting the output, converting it and writing it out. Hope that helps!
public static void main(String... args) {
final File file = new File("inpath.png");
final File out = new File("outpath.png");
// This is just sugar for the point of illustration.
// The purpose here is just to have access to a Context
ImageJ imagej = new ImageJ();
// Cache the context for future use.
Context context = imagej.getContext();
try {
// Use the context to get the services we want to ensure they are all
// properly initialized.
// If this was a Command these could all be #Parameters to be populated
// automatically.
DatasetService datasetService = context.getService(DatasetService.class);
CommandService commandService = context.getService(CommandService.class);
DatasetIOService datasetIOService =
context.getService(DatasetIOService.class);
Dataset input = datasetIOService.open(file.getAbsolutePath());
// Start the command
Future<CommandModule> future =
commandService.run(Binarize.class, true, "inputData", input);
// Get the command output
Dataset binarized = (Dataset) future.get().getOutput("outputMask");
// The output type is a binary image which, at the moment, needs to be
// explicitly converted to something that can be written out.
// Adapted from:
// http://fiji.sc/ImgLib2_Examples#Example_2c_-_Generic_copying_of_image_data
Img inputImg = input.getImgPlus().getImg();
Img outputImg = binarized.getImgPlus().getImg();
Img typedImg =
inputImg.factory().create(inputImg, inputImg.firstElement());
scale(outputImg, typedImg);
Dataset output = datasetService.create(typedImg);
// Save the output dataset
datasetIOService.save(output, out.getAbsolutePath());
}
catch (IOException exc) {
exc.printStackTrace();
}
catch (InterruptedException exc) {
exc.printStackTrace();
}
catch (ExecutionException exc) {
exc.printStackTrace();
}
finally {
// Dispose of the context to shut down
context.dispose();
}
}
public static <T extends IntegerType<T>> void scale(
final RandomAccessible<BitType> source, final IterableInterval<T> target)
{
// create a cursor that automatically localizes itself on every move
Cursor<T> targetCursor = target.localizingCursor();
RandomAccess<BitType> sourceRandomAccess = source.randomAccess();
// iterate over the input cursor
while (targetCursor.hasNext()) {\
// move input cursor forward
targetCursor.fwd();
// set the output cursor to the position of the input cursor
sourceRandomAccess.setPosition(targetCursor);
// set the value of this pixel of the output image
BitType b = sourceRandomAccess.get();
if (b.get()) {
targetCursor.get().setOne();
}
else {
targetCursor.get().setZero();
}
}
}
I have a problem with some Java servlet I developed a few days ago and finally uploaded it to my test application server.
First I will describe what the Servlet does: Basically it creates a website counter by using two parameters: First parameter is the usernames identifier (username) and the second one is an integer identity which identifies the background image and design which should be applied to the counter, so that the user can change the used counter design without losing his counts. I use a Java servlet to generate the PNG file in these steps:
Load counter and design from database to obtain data
Load background image from PNG image source file to a BufferedImage
Draw the current counter value to the background image using the specified font, xPos, yPos, font size, etc..
Write the BufferedImage as PNG file to the servlet output stream specifying the content type is PNG.
The problem here is that the image which will be generated is incomplete (!). I do a flush and close on the output stream buy anyway it is still incomplete. I also tried to leave out all my modifications to draw anything on the Graphics2D of the BufferedImage, but it did not help at all. When I run the same code base in a stand alone application the output image is just fine so I don't think there is a problem with ImageIO or my general code.
Perhaps I missed some mandatory stuff in the servlet output declaration, but I can't find the bug on my own.
Here is the real running application:
http://csz-online.net:8080/CounterBuilder/CounterServlet?name=clemens&layout=61
As you can see the file in incomplete! Firefox for example shows the PNG content it received, but if I download the file Eye of Gnome for example won't display the corrupt file at all! The same image created by the stand alone application is complete and works fine.
THANKS!
Here is the code of the Java servlet processRequest method, which is to be executed on post and get. Since I don't have the original source code at the moment this is what JD-GUI outputs. It pretty looks the same except the exception handling at the end of the method:
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
ServletContext cntx = getServletContext();
try
{
String creationStr = request.getParameter("layout");
long cId = Long.valueOf(creationStr).longValue();
String name = request.getParameter("name");
Counter counter = this.counterFacade.findByNickname(name);
Creation creation = this.creationFacade.find(Long.valueOf(cId));
String filename = cntx.getRealPath(creation.getBackground());
if (filename == null) {
throw new FileNotFoundException("BG not found: " + creation.getBackground());
}
String mime = cntx.getMimeType(filename);
int cCounter = counter.getCounter() + 1;
response.setContentType(mime);
File file = new File(filename);
response.setContentLength((int)file.length());
OutputStream out = response.getOutputStream();Throwable localThrowable4 = null;
try
{
String fontStr = creation.getFont();
BufferedImage image = ImageIO.read(file);
BufferedImage[] digits = new BufferedImage[10];
image.flush();
switch (fontStr)
{
case "gold":
Graphics2D g = image.createGraphics();
digits[0] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/0.png")));
digits[1] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/1.png")));
digits[2] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/2.png")));
digits[3] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/3.png")));
digits[4] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/4.png")));
digits[5] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/5.png")));
digits[6] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/6.png")));
digits[7] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/7.png")));
digits[8] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/8.png")));
digits[9] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/9.png")));
drawNumbersToImage(g, digits, cCounter, creation.getxPos(), creation.getyPos());
break;
default:
Font font = new Font(creation.getFont(), 0, creation.getFontSize());
FontRenderContext frc = new FontRenderContext(null, true, true);
TextLayout layout = new TextLayout(String.valueOf(cCounter), font, frc);
Graphics2D h = image.createGraphics();
h.setColor((Color)Color.class.getField(creation.getColor()).get(null));
layout.draw(h, creation.getxPos(), creation.getyPos());
h.dispose();
}
counter.setCounter(cCounter);
this.counterFacade.edit(counter);
ImageIO.write(image, "PNG", out);
out.flush();
out.close();
}
catch (Throwable localThrowable1)
{
localThrowable4 = localThrowable1;throw localThrowable1;
}
finally
{
if (out != null) {
if (localThrowable4 != null) {
try
{
out.close();
}
catch (Throwable x2)
{
localThrowable4.addSuppressed(x2);
}
} else {
out.close();
}
}
}
}
catch (IOException|NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException ex)
{
ex.printStackTrace();
response.setContentType("text/HTML");
PrintWriter writer = response.getWriter();Throwable localThrowable3 = null;
try
{
writer.print("Could not generate counter due to missing or invalid parameters <b>bg</b> and <b>name</b>.");
}
catch (Throwable localThrowable2)
{
localThrowable3 = localThrowable2;throw localThrowable2;
}
finally
{
if (writer != null) {
if (localThrowable3 != null) {
try
{
writer.close();
}
catch (Throwable x2)
{
localThrowable3.addSuppressed(x2);
}
} else {
writer.close();
}
}
}
}
}