I want to use processing to take screenshots and use the screenshot to generate animations. The way I try to do it is save the frames into a directory and reload the image files into an image String. Not sure if it is the right way to do it, but I get error when I load the images:
error message
String[] listFileNames(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
String names[] = file.list();
return names;
} else {
// If it's not a directory
return null;
}
}
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
names = listFileNames("/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/");
print(names);
if(names.length != 0){
for(int i = 0; i < names.length; i = i+1){
img[i] = loadImage(names[i]);
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
}
I wonder if anyone knows the problem and if there is another way to do the screenshot animations.Thank you!
I think the issue is that listFileNames() simply list the file names, not the absolute paths to the files. Unless those images are in the sketch directory (or within the data directory of the sketch) you will need to use the full path to the images to load them.
One option would be to concatenate the absolute directory path with the filename:
String[] listFileNames(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
String names[] = file.list();
return names;
} else {
// If it's not a directory
return null;
}
}
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
String dir = "/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"
names = listFileNames(dir);
print(names);
if(names.length != 0){
for(int i = 0; i < names.length; i = i+1){
// concatenate the directory with each fine name
// dir inclues / at the end (otherwise you can use File.separator (e.g. dir/image.png = dir + File.separator + "image.png"))
img[i] = loadImage(dir + names[i]);
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
}
You could use listFiles() instead which returns File objects that do provide the absolute path.
Here's a snippet illustrating usage of listFiles():
File[] listFiles(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
File[] files = file.listFiles();
return files;
} else {
// If it's not a directory
return null;
}
}
File[] files;
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
String dir = "/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"
files = listFiles(dir);
if(files == null) {
println(dir + "is not a directory");
return;
}
for(int i = 0; i < files.length; i = i+1){
// concatenate the directory with each fine name
// dir inclues / at the end (otherwise you can use File.separator (e.g. dir/image.png = dir + File.separator + "image.png"))
img[i] = loadImage(files[i].getAbsolutePath());
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
You can find this function and a few more under Daniel Shiffman's DirectoryList example via Processing > Examples > Topics > File IO
Additionally, listFiles() / listPaths() are present in Processing already, just a bit tucked away. The nice thing about these variants is that you can filter results to be files with png extension only (e.g. excluding folder, hidden files, etc.).
Here's a modified version of the above snippet:
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
String dir = "/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"
File[] files = listFiles(dir,"files","extension=png")
if(files == null) {
println(dir + "is not a directory");
return;
}
for(int i = 0; i < files.length; i = i+1){
// concatenate the directory with each fine name
// dir inclues / at the end (otherwise you can use File.separator (e.g. dir/image.png = dir + File.separator + "image.png"))
img[i] = loadImage(files[i].getAbsolutePath());
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
A bit off-topic from your question I am noticing a few confusing things:
you're listing files and loading images multiple times per frame in draw(). You might want to load the images once in setup(), store them in a PImage[], then simply use them in draw()
It appears you're updating particles only while loading images and there doesn't seem to be a connection between the loaded images and the particles from the code you're shared. You might want to separate the particle code from the image loading code
Shameless plug: If you want to easily load and display png sequences in Processing you can use the ImageSequencePlayer Processing library I worked on:
// import library
import com.hirschandmann.image.*;
// create a reference to an image sequence
ISPlayer player;
void setup() {
size(640, 360);
// create instance and load all valid images from the data folder
player = new ISPlayer(this,"/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"));
}
void draw() {
// clear background with yellow
background(255, 204, 0);
// render image sequence, ISPlayer extends PImage, use it as such
image(player,mouseX,mouseY);
}
// image sequence events
// all frames loaded
void onSequenceLoaded(ISPlayer player){
println(player+" sequence fully loaded");
player.loop();
}
Related
I have a Sketch in Processing written in P5.js, but I don't know how to implement user's input.
The program needs to get .jpg image and it's working on it.
Every my attempt of user-input implementation ends with blank screen or endless "Loading..." screen.
Below is example with preloaded image (I need user to do it).
let img;
let size;
let pixels = [];
function preload(){
img=loadImage('image.jpg');
}
function setup() {
img.resize(windowHeight,0);
size = img.height/2;
createCanvas(img.width, img.height);
background(0);
makePixels();
drawPortrait();
}
function makePixels(){
for(let y=0; y<height; y+=size){
for(let x=0; x<width; x+=size){
let c = img.get(x,y);
tint(c);
pixels.push ( new Pixel (x,y,size,c) );
}
}
}
function drawPortrait(){
for(let p of pixels){
p.show();
}
}
function drawLastFour(){
for(let i = pixels.length-4; i<pixels.length; i++){
pixels[i].show();
}
}
function mouseMoved(){
for(let i = 0; i<pixels.length; i++){
if( (mouseX > pixels[i].x) && (mouseX <= pixels[i].x+pixels[i].s) && (mouseY > pixels[i].y) && (mouseY <= pixels[i].y+pixels[i].s) ){
for(let py = pixels[i].y; py<pixels[i].y+pixels[i].s; py+=pixels[i].s/2){
for(let px = pixels[i].x; px<pixels[i].x+pixels[i].s; px+=pixels[i].s/2){
let c = img.get(px, py);
pixels.push( new Pixel(px,py,pixels[i].s/2, c) );
}
}
pixels.splice(i,1);
break;
}
}
drawLastFour();
}
You can use the createFileInput function to create an input element of type file. Your user can then select an image file, which can be used by your sketch. Here is the (slightly modified) example code that shows how you can use it:
let inputElement;
let userImage;
function setup() {
inputElement = createFileInput(handleFile);
inputElement.position(0, 0);
}
function draw() {
background(255);
if (userImage != null) {
image(userImage, 0, 0, width, height);
}
}
function handleFile(file) {
print(file);
if (file.type === 'image') {
userImage = createImg(file.data, '');
userImage.hide();
} else {
userImage = null;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
I am trying to load a world(kind of..) into webglfrom an external file which has information of vertex position and face positions but the problem is the file containing the data is very large..(about 100mb). In my approach I am using the file as buffer and have a single buffer in the init buffer which is over-written again and again. What I am doing is, I am reading the values for an object from the file and drawing it on the canvas, then over-writing the buffer with the data of other object in my scene and adding it to the scene. In short I am not saving the vertex and face information. While animating I am reading the entire file again and re-drawing. Its working fine with a file size of 20mb. but for file of large size I am not able to use high frame rate while animating. Which is not looking good.
My question is should I put all the vertex information into buffer and then draw the graphics and forget about the file…or my approach can be optimized…? Also if you can suggest any other method then it would be really helpful
try {
var fileInput = document.getElementById('fileInput');
var file = fileInput.files[0];
// read from filename
var reader = new FileReader();
reader.onload = function (e) {
var count=0;
var lastline=0;
var i;
var j;
var text = reader.result;
var lines = text.split("\r\n");
while(lastline<lines.length)
{
var vertices = [];
var VertexIndices = [];
var vertexNormals=[];
/////Position of the objects
for (i = lastline; i < lines.length; i++) {
if (lines[i] == "MESH_FACE_POSITION_LIST {") {
break;
}
}
for (j = i + 1; j < lines.length; j++) {
if (lines[j] == "}") {
break;
}
else {
var currentvertices = lines[j].split(" ");
for (var k = 0; k < currentvertices.length; k++) {
VertexIndices.push(parseInt(currentvertices[k]));//Check for ","
}
}
}
noOfVerticesForTriangles = VertexIndices.length;
for (i = j; i < lines.length; i++) {
if (lines[i] == "MODEL_POSITION_LIST {") {
break;
}
}
for (j = i + 1; j < lines.length; j++) {
if (lines[j] == "}") {
break;
}
else {
var currentvertices = lines[j].split(" ");
for (var k = 0; k < currentvertices.length; k++) {
vertices.push(parseFloat(currentvertices[k]));//Check for ","
}
}
}
noOfVertices = vertices.length / 3;
lastline=j;
//this is where i am calling the function to draw the graphics after reading the data for an object
initBuffers(vertices,VertexIndices);
drawScene();
}
}
reader.readAsText(file);
}
catch (e) {
}
}
Code for init buffer
function initBuffers(vertices,VertexIndices) {
vertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
vertexPositionBuffer.itemSize = 3;
vertexPositionBuffer.numItems = noOfVertices;
vertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(VertexIndices), gl.STATIC_DRAW);
vertexIndexBuffer.itemSize = 1;
vertexIndexBuffer.numItems = noOfVerticesForTriangles;
}
My question is should I put all the vertex information into buffer and
then draw the graphics and forget about the file…
Yes, this is pretty much how 3d works :)
I have figured out how to move my character around the maze using the algorithm I have written, but the count is not figuring correctly. At the end of each row my character moves up and down several times until the count reaches the specified number to exit the loop, then the character moves along the next row down until it reaches the other side and repeats the moving up and down until the count reaches the specified number again. Can anyone help me find why my count keeps getting off? The algorithm and the maze class I am calling from is listed below.
public class P4 {
public static void main(String[] args) {
// Create maze
String fileName = args[3];
Maze maze = new Maze(fileName);
System.out.println("Maze name: " + fileName);
// Get dimensions
int mazeWidth = maze.getWidth();
int mazeHeight = maze.getHeight();
// Print maze size
System.out.println("Maze width: " + mazeWidth);
System.out.println("Maze height: " + mazeHeight);
int r = 0;
int c = 0;
// Move commands
while (true){
for (c = 0; c <= mazeWidth; c++){
if (maze.moveRight()){
maze.isDone();
c++;
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveRight() == false && c != mazeWidth){
maze.moveDown();
maze.moveRight();
maze.moveRight();
maze.moveUp();
c++;
}
}
for (r = 0; r % 2 == 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
for (c = mazeWidth; c >= 0; c--){
if (maze.moveLeft()){
c--;
maze.isDone();
System.out.println(c);
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveLeft() == false && c != 0){
maze.moveDown();
maze.moveLeft();
maze.moveLeft();
maze.moveUp();
c--;
}
}
for (r = 1; r % 2 != 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
}
}
}
public class Maze {
// Maze variables
private char mazeData[][];
private int mazeHeight, mazeWidth;
private int finalRow, finalCol;
int currRow;
private int currCol;
private int prevRow = -1;
private int prevCol = -1;
// User interface
private JFrame frame;
private JPanel panel;
private Image java, student, success, donotpass;
private ArrayList<JButton> buttons;
// Maze constructor
public Maze(String fileName) {
// Read maze
readMaze(fileName);
// Graphics setup
setupGraphics();
}
// Get height
public int getHeight() {
return mazeHeight;
}
// Get width
public int getWidth() {
return mazeWidth;
}
// Move right
public boolean moveRight() {
// Legal move?
if (currCol + 1 < mazeWidth) {
// Do not pass?
if (mazeData[currRow][currCol + 1] != 'D')
{
currCol++;
redraw(true);
return true;
}
}
return false;
}
// Move left
public boolean moveLeft() {
// Legal move?
if (currCol - 1 >= 0) {
// Do not pass?
if (mazeData[currRow][currCol - 1] != 'D')
{
currCol--;
redraw(true);
return true;
}
}
return false;
}
// Move up
public boolean moveUp() {
// Legal move?
if (currRow - 1 >= 0) {
// Do not pass?
if (mazeData[currRow - 1][currCol] != 'D')
{
currRow--;
redraw(true);
return true;
}
}
return false;
}
// Move down
public boolean moveDown() {
// Legal move?
if (currRow + 1 < mazeHeight) {
// Do not pass?
if (mazeData[currRow + 1][currCol] != 'D')
{
currRow++;
redraw(true);
return true;
}
}
return false;
}
public boolean isDone() {
// Maze solved?
if ((currRow == finalRow) && (currCol == finalCol))
return true;
else
return false;
}
private void redraw(boolean print) {
// Wait for awhile
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
if (print)
System.out.println("Moved to row " + currRow + ", column " + currCol);
// Compute index and remove icon
int index = (prevRow * mazeWidth) + prevCol;
if ((prevRow >= 0) && (prevCol >= 0)) {
buttons.get(index).setIcon(null);
}
// Compute index and add icon
index = (currRow * mazeWidth) + currCol;
if ((currRow == finalRow) && (currCol == finalCol))
buttons.get(index).setIcon(new ImageIcon(success));
else
buttons.get(index).setIcon(new ImageIcon(student));
// Store previous location
prevRow = currRow;
prevCol = currCol;
}
// Set button
private void setButton(JButton button, int row, int col) {
if (mazeData[row][col] == 'S') {
button.setIcon(new ImageIcon(student));
currRow = row;
currCol = col;
} else if (mazeData[row][col] == 'J') {
button.setIcon(new ImageIcon(java));
finalRow = row;
finalCol = col;
} else if (mazeData[row][col] == 'D') {
button.setIcon(new ImageIcon(donotpass));
}
}
// Read maze
private void readMaze(String filename) {
try {
// Open file
Scanner scan = new Scanner(new File(filename));
// Read numbers
mazeHeight = scan.nextInt();
mazeWidth = scan.nextInt();
// Allocate maze
mazeData = new char[mazeHeight][mazeWidth];
// Read maze
for (int row = 0; row < mazeHeight; row++) {
// Read line
String line = scan.next();
for (int col = 0; col < mazeWidth; col++) {
mazeData[row][col] = line.charAt(col);
}
}
// Close file
scan.close();
} catch (IOException e) {
System.out.println("Cannot read maze: " + filename);
System.exit(0);
}
}
// Setup graphics
private void setupGraphics() {
// Create grid
frame = new JFrame();
panel = new JPanel();
panel.setLayout(new GridLayout(mazeHeight, mazeWidth, 0, 0));
frame.add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH);
frame.add(panel, BorderLayout.CENTER);
// Look and feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Configure window
frame.setSize(mazeWidth * 100, mazeHeight * 100);
frame.setTitle("Maze");
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setAlwaysOnTop(true);
// Load and scale images
ImageIcon icon0 = new ImageIcon("Java.jpg");
Image image0 = icon0.getImage();
java = image0.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon1 = new ImageIcon("Student.jpg");
Image image1 = icon1.getImage();
student = image1.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon2 = new ImageIcon("Success.jpg");
Image image2 = icon2.getImage();
success = image2.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon3 = new ImageIcon("DoNotPass.jpg");
Image image3 = icon3.getImage();
donotpass = image3.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
// Build panel of buttons
buttons = new ArrayList<JButton>();
for (int row = 0; row < mazeHeight; row++) {
for (int col = 0; col < mazeWidth; col++) {
// Initialize and add button
JButton button = new JButton();
Border border = new LineBorder(Color.darkGray, 4);
button.setOpaque(true);
button.setBackground(Color.gray);
button.setBorder(border);
setButton(button, row, col);
panel.add(button);
buttons.add(button);
}
}
// Show window
redraw(false);
frame.setVisible(true);
}
}
One error I can see in your code is that you're incrementing your c counter more often than you should. You start with it managed by your for loop, which means that it will be incremented (or decremented, for the leftward moving version) at the end of each pass through the loop. However, you also increment it an additional time in two of your if statements. That means that c might increase by two or three on a single pass through the loop, which is probably not what you intend.
Furthermore, the count doesn't necessarily have anything obvious to do with the number of moves you make. The loop code will always increase it by one, even if you're repeatedly trying to move through an impassible wall.
I don't really understand what your algorithm is supposed to be, so I don't have any detailed advice for how to fix your code.
One suggestion I have though is that you probably don't ever want to be calling methods on your Maze class without paying attention to their return values. You have a bunch of places where you call isDone but ignore the return value, which doesn't make any sense. Similarly, you should always be checking the return values from your moveX calls, to see if the move was successful or not. Otherwise you may just blunder around a bunch, without your code having any clue where you are in the maze.
Using processing I am trying to run a script that will process a folder full of frames.
The script is a combination of PixelSortFrames and SortThroughSeamCarving.
I am new to processing and what I want does not seems to be working. I would like the script to run back through and choose the following file in the folder to be processed. At the moment it stops at the end and does not return to start on next file (there are three other modules also involved).
Any help would be much appreciated. :(
/* ASDFPixelSort for video frames v1.0
Original ASDFPixelSort by Kim Asendorf <http://kimasendorf.com>
https://github.com/kimasendorf/ASDFPixelSort
Fork by dx <http://dequis.org> and chinatsu <http://360nosco.pe>
// Main configuration
String basedir = ".../Images/Seq_002"; // Specify the directory in which the frames are located. Use forward slashes.
String fileext = ".jpg"; // Change to the format your images are in.
int resumeprocess = 0; // If you wish to resume a previously stopped process, change this value.
boolean reverseIt = true;
boolean saveIt = true;
int mode = 2; // MODE: 0 = black, 1 = bright, 2 = white
int blackValue = -10000000;
int brightnessValue = -1;
int whiteValue = -6000000;
// -------
PImage img, original;
float[][] sums;
int bottomIndex = 0;
String[] filenames;
int row = 0;
int column = 0;
int i = 0;
java.io.File folder = new java.io.File(dataPath(basedir));
java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(fileext);
}
};
void setup() {
if (resumeprocess > 0) {i = resumeprocess - 1;frameCount = i;}
size(1504, 1000); // Resolution of the frames. It's likely there's a better way of doing this..
filenames = folder.list(extfilter);
size(1504, 1000);
println(" " + width + " x " + height + " px");
println("Creating buffer images...");
PImage hImg = createImage(1504, 1000, RGB);
PImage vImg = createImage(1504, 1000, RGB);
// draw image and convert to grayscale
if (i +1 > filenames.length) {println("Uh.. Done!"); System.exit(0);}
img = loadImage(basedir+"/"+filenames[i]);
original = loadImage(basedir+"/"+filenames[i]);
image(img, 0, 0);
filter(GRAY);
img.loadPixels(); // updatePixels is in the 'runKernals'
// run kernels to create "energy map"
println("Running kernals on image...");
runKernels(hImg, vImg);
image(img, 0, 0);
// sum pathways through the image
println("Getting sums through image...");
sums = getSumsThroughImage();
image(img, 0, 0);
loadPixels();
// get start point (smallest value) - this is used to find the
// best seam (starting at the lowest energy)
bottomIndex = width/2;
// bottomIndex = findStartPoint(sums, 50);
println("Bottom index: " + bottomIndex);
// find the pathway with the lowest information
int[] path = new int[height];
path = findPath(bottomIndex, sums, path);
for (int bi=0; bi<width; bi++) {
// get the pixels of the path from the original image
original.loadPixels();
color[] c = new color[path.length]; // create array of the seam's color values
for (int i=0; i<c.length; i++) {
try {
c[i] = original.pixels[i*width + path[i] + bi]; // set color array to values from original image
}
catch (Exception e) {
// when we run out of pixels, just ignore
}
}
println(" " + bi);
c = sort(c); // sort (use better algorithm later)
if (reverseIt) {
c = reverse(c);
}
for (int i=0; i<c.length; i++) {
try {
original.pixels[i*width + path[i] + bi] = c[i]; // reverse! set the pixels of the original from sorted array
}
catch (Exception e) {
// when we run out of pixels, just ignore
}
}
original.updatePixels();
}
// when done, update pixels to display
updatePixels();
// display the result!
image(original, 0, 0);
if (saveIt) {
println("Saving file...");
//filenames = stripFileExtension(filenames);
save("results/SeamSort_" + filenames + ".tiff");
}
println("DONE!");
}
// strip file extension for saving and renaming
String stripFileExtension(String s) {
s = s.substring(s.lastIndexOf('/')+1, s.length());
s = s.substring(s.lastIndexOf('\\')+1, s.length());
s = s.substring(0, s.lastIndexOf('.'));
return s;
}
This code works by processing all images in the selected folder
String basedir = "D:/things/pixelsortframes"; // Specify the directory in which the frames are located. Use forward slashes.
String fileext = ".png"; // Change to the format your images are in.
int resumeprocess = 0; // If you wish to resume a previously stopped process, change this value.
int mode = 1; // MODE: 0 = black, 1 = bright, 2 = white
int blackValue = -10000000;
int brightnessValue = -1;
int whiteValue = -6000000;
PImage img;
String[] filenames;
int row = 0;
int column = 0;
int i = 0;
java.io.File folder = new java.io.File(dataPath(basedir));
java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(fileext);
}
};
void setup() {
if (resumeprocess > 0) {i = resumeprocess - 1;frameCount = i;}
size(1920, 1080); // Resolution of the frames. It's likely there's a better way of doing this..
filenames = folder.list(extfilter);
}
void draw() {
if (i +1 > filenames.length) {println("Uh.. Done!"); System.exit(0);}
row = 0;
column = 0;
img = loadImage(basedir+"/"+filenames[i]);
image(img,0,0);
while(column < width-1) {
img.loadPixels();
sortColumn();
column++;
img.updatePixels();
}
while(row < height-1) {
img.loadPixels();
sortRow();
row++;
img.updatePixels();
}
image(img,0,0);
saveFrame(basedir+"/out/"+filenames[i]);
println("Frames processed: "+frameCount+"/"+filenames.length);
i++;
}
essentially I want to do the same thing only with a different image process but my code is not doing this to all with in the folder... just one file.
You seem to be confused about what the setup() function does. It runs once, and only once, at the beginning of your code's execution. You don't have any looping structure for processing the other files, so it's no wonder that it only processes the first one. Perhaps wrap the entire thing in a for loop? It looks like you kind of thought about this, judging by the global variable i, but you never increment it to go to the next image and you overwrite its value in several for loops later anyway.
As i have been asked to automate our Company's website using Selenium Automation tooL.
But i am new to Selenium tool to proceed with, but i have learnt the basics of Selenium IDE and RC. But i am very much confused with how to compare actual and original images as we usually do in other automation tools. How do we come to a result that there bug in the website? Its obviously through image comparison but i wonder as selenium is one of the very popular tools but it doesn't have image comparing option. On the other hand i doubt whether my way of proceeding with the automation process is correct! Could somebody please help me out..
Thanks in Advance!!
Sanjay S
I had simillar task. I needed to compare more than 3000 images on a WebPage.
First of all I scrolled page to load all images:
public void compareImage() throws InterruptedException {
driver.get(baseUrl);
driver.manage().window().maximize();
JavascriptExecutor executor = (JavascriptExecutor) driver;
Long previousHeight;
Long currentHeight;
do {
previousHeight = (Long) executor.executeScript("return document.documentElement.scrollHeight");
executor.executeScript("window.scrollBy(0, document.documentElement.scrollHeight)");
Thread.sleep(500);
currentHeight = (Long) executor.executeScript("return document.documentElement.scrollHeight");
} while (Long.compare(previousHeight, currentHeight) != 0);
after I compared size of all images with first image(or you can just write size):
List<WebElement> images = driver.findElements(By.cssSelector("img[class='playable']"));
List<String> errors = new LinkedList<>();
int imgWidth, imgHeight, elWidth, elHeight;
int imgNum = 0;
imgWidth = images.get(0).getSize().getWidth();
imgHeight = images.get(0).getSize().getHeight();
for (WebElement el : images) {
imgNum++;
elWidth = el.getSize().getWidth();
elHeight = el.getSize().getHeight();
if (imgWidth != elWidth || imgHeight != elHeight) {
errors.add(String.format("Picture # %d has incorrect size (%d : %d) px"
, imgNum, elWidth, elHeight));
}
}
for (String str : errors)
System.out.println(str);
if (errors.size() == 0)
System.out.println("All images have the same size");
}
Since you mention knowledge about Selenium RC, you can easily extend Selenium's capability using a library for your chosen programming language. For instance, in Java you can use the PixelGrabber class for comparing two images and assert their match.
imagemagick and imagediff are also two good tools to use for image matching. You would require Selenium RC and a programming language knowledge to work with it.
Image comparison on C#. To get exact results I recommend to disable anti aliasing browser feature before taking screenshots, otherwise pixels each time are a little bit different drawn. For example HTML canvas element options.AddArgument("disable-canvas-aa");
private static bool ImageCompare(Bitmap bmp1, Bitmap bmp2, Double TolerasnceInPercent)
{
bool equals = true;
bool flag = true; //Inner loop isn't broken
//Test to see if we have the same size of image
if (bmp1.Size == bmp2.Size)
{
for (int x = 0; x < bmp1.Width; ++x)
{
for (int y = 0; y < bmp1.Height; ++y)
{
Color Bitmap1 = bmp1.GetPixel(x, y);
Color Bitmap2 = bmp2.GetPixel(x, y);
if (Bitmap1.A != Bitmap2.A)
{
if (!CalculateTolerance(Bitmap1.A, Bitmap2.A, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.R != Bitmap2.R)
{
if (!CalculateTolerance(Bitmap1.R, Bitmap2.R, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.G != Bitmap2.G)
{
if (!CalculateTolerance(Bitmap1.G, Bitmap2.G, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.B != Bitmap2.B)
{
if (!CalculateTolerance(Bitmap1.B, Bitmap2.B, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
}
if (!flag)
{
break;
}
}
}
else
{
equals = false;
}
return equals;
}
This C# function calculates tolerance
private static bool CalculateTolerance(Byte FirstImagePixel, Byte SecondImagePixel, Double TolerasnceInPercent)
{
double OneHundredPercent;
double DifferencesInPix;
double DifferencesPercentage;
if (FirstImagePixel > SecondImagePixel)
{
OneHundredPercent = FirstImagePixel;
}
else
{
OneHundredPercent = SecondImagePixel;
}
if (FirstImagePixel > SecondImagePixel)
{
DifferencesInPix = FirstImagePixel - SecondImagePixel;
}
else
{
DifferencesInPix = SecondImagePixel - FirstImagePixel;
}
DifferencesPercentage = (DifferencesInPix * 100) / OneHundredPercent;
DifferencesPercentage = Math.Round(DifferencesPercentage, 2);
if (DifferencesPercentage > TolerasnceInPercent)
{
return false;
}
return true;
}