CPIT-380 Group Project

Supervised with care by Doctor Saim Ahmed Rasheed

Show our work 

Color change

In the first part was fairly easy and staightfoward in this project we used alot of Picture[1] and SimplePicture[2] classes and methods to manipulate the colors of the image.

The method that we used:

    private void updateColors() {
        Pixel[] pixelArray = pic.getPixels();
        for (Pixel pixelObj : pixelArray) {
            if (R_Value != -1) {
                pixelObj.setRed((int) (pixelObj.getRed() * (R_Value / 50.0)));
            }
            if (G_Value != -1) {
                pixelObj.setGreen((int) (pixelObj.getGreen() * (G_Value / 50.0)));
            }
            if (B_Value != -1) {
                pixelObj.setBlue((int) (pixelObj.getBlue() * (B_Value / 50.0)));
            }
        }
        redSlider.setValue(50);
        greenSlider.setValue(50);
        blueSlider.setValue(50);
        updateIMG();
    }

Copy and crop

This part is about cropping some image, in order to do that with a user friendly interface we used mouse click listener that is built-in java when the user wants to crop an image we will wait until he chooses two points on the image then we will send those points coordinates (x,y) to a method that will do the cropping and display the image. 
The issue that I faced was that the picture height and width in the image that is shown in the GUI is not the actual height and width it either got scaled down or up to be displayed properly. and the mouse coordinates that we get from the user will be based on the not correct height and width that's why we have to convert it first, not a big issue i was just confused because i didn't know what was going on.

The methods that we used:

// Crop image button
    private void cropImgActionPerformed(java.awt.event.ActionEvent evt) {                                        
        // TODO add your handling code here:
        // image cropped button
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image please!", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            JFrame parent = new JFrame();
            JOptionPane.showMessageDialog(parent, "Please click on two points in the image");

            imgLabel.addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    System.out.println("Clicked!");
                    System.out.println(e.getX());
                    System.out.println(e.getY());
                    numOfClicks++;
                    if (numOfClicks == 1) {
                        x1 = e.getX();
                        y1 = e.getY();
                    } else if (numOfClicks == 2) {
                        x2 = e.getX();
                        y2 = e.getY();
                        // call the crop method with both cordinates.
                        CropImage(x1, y1, x2, y2);
                        numOfClicks = 0;
                        imgLabel.removeMouseListener(this);
                    }
                }
            });
        }
    }       
// Crop image method
    private void CropImage(int x1, int y1, int x2, int y2) {

        double W = (pic.getWidth() * 1.00 / targetLabel.getWidth());
        double H = (pic.getHeight() * 1.00 / targetLabel.getHeight());

        x1 = (int) (W * x1);
        x2 = (int) (W * x2);
        y1 = (int) (H * y1);
        y2 = (int) (H * y2);

        Picture newPic = new Picture(sourcePicture.getWidth(), sourcePicture.getHeight());

        Pixel sourcePixel;
        Pixel targetPixel;

        for (int i = x1; i < x2; i++) {
            for (int j = y1; j < y2; j++) {
                sourcePixel = sourcePicture.getPixel(i, j);
                targetPixel = newPic.getPixel(i, j);
                targetPixel.setColor(sourcePixel.getColor());
            }

        }
        pic = newPic;
        updateIMG();

    }

RGB to Gray

The code will store all the pixels in array and will go through each pixel to get the RGB values and divide it on 3, save the result in variable then set the pixel RGB to this result.

The methods that we used:

private void ConvertToGrayScale(java.awt.event.ActionEvent evt) {                                    
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image please!", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            Pixel[] pxl = pic.getPixels();
            for (int i = 0; i < pxl.length; i++) {
                int avg = (pxl[i].getRed() + pxl[i].getBlue() + pxl[i].getGreen()) / 3;
                pxl[i].setColor(new Color(avg, avg, avg));
            }
            updateIMG();
        }
    } 

Gray scale to bianry using thresholding

this method is about converting the gray scale image into binary image. First the user will enter the thresholding value, then a copy of the current picture will be created and then will go through each pixel to get the average of each pixel and save it in a variable , in case the threshold value is higher than the average the pixel will turn into black, otherwise it will turn white.

The methods that we used:

// this method is in picture class
    public Picture gray2Binary(int threshold) {

        Picture target = new Picture(this.getWidth(), this.getHeight());
        int avar;
        for (int x = 0; x < this.getWidth(); x++) {
            for (int y = 0; y < this.getHeight(); y++) {
                avar = (int) Math.abs(this.getPixel(x, y).getAverage());
                if (avar < threshold) {
                    target.getPixel(x, y).setColor(Color.BLACK);
                }
            }
        }
        return target;
    }

RGB Colored image to HSV

Unfortunately, this method did not work with us, we have tried to implement multiple codes and formulas but with no luck. the above formulas[3]


Blending two images

First you should upload picture then we will check if the pic object not null if that true we will initialize filechooser to upload another picture then will the program will store all the pixels of two picture and will go trough each pixels to get average for all picture then we will get the average of RBG of each picture add them together then divide by number of pictures then set all the pixels average result in picture 1 object.

The methods that we used:

    private void BlendActionPerformed(java.awt.event.ActionEvent evt) {                                      
        if (pic != null) {
            JFileChooser jFileChooser = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
            jFileChooser.showOpenDialog(jFileChooser);
            Picture picture_obj = new Picture(jFileChooser.getSelectedFile().getAbsolutePath());
            if ((pic.getWidth() * pic.getHeight()) == (picture_obj.getWidth() * picture_obj.getHeight())) {
                Pixel[] pic_1 = pic.getPixels();
                Pixel[] pic_2 = picture_obj.getPixels();
                int num_Colors = 3;
                int i = 0;
                while (i < pic_1.length) {
                    //----Here we calc the Colors----
                    int valueP1 = ((pic_1[i].getRed() + pic_1[i].getGreen() + pic_1[i].getBlue()) / num_Colors);
                    int valueP2 = ((pic_2[i].getRed() + pic_2[i].getGreen() + pic_2[i].getBlue()) / num_Colors);
                    //----Here we calc avg for 2 pic----
                    int avg_Colors = (int) (valueP1 + valueP2) / 2;
                    pic_1[i].setColor(new Color(avg_Colors));
                    i++;
                }
                updateIMG();
            } else {
                JOptionPane.showMessageDialog(null, "the image should be the same size!");
            }
            updateIMG();
        } else {
            JOptionPane.showMessageDialog(null, "Select an image !", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }   

Rotating left and right and 180

Rotate Left:
First you should upload a picture then we call rotate left method, the method will get height and width after that create 2 variables for source and target then will make 2 loops to move on width and height then take the pixel in the srcPic on (x,y) and take the pixel in the targetPic but swap between x and y after that use the formula of left (y,w-1-x), in the pixel of the target we change the color with the color of the pixel src.
Rotate Right :
First you shuold upload picture then we call rotate right method, the method will get height and width after that create 2 variables for source and target then wil make 2 loops to move on width and height then take the pixel in the srcPic on (x,y) and take the pixel in the targetPic but swap between x and y after that use the formula of Rright (H - 1 -y,x), in the pixel of the target we change the color with color of the pixel src.
Rotate 180:
I used prebuilt method in picture class and we use the following formula
Target y = source height - 1 -source  y ,Target x = source height - 1 -source x .

The methods that we used:

    public Picture rotateLeft() {
        Picture target = new Picture(this.getHeight(), this.getWidth());
        Pixel sourcePixel = null;
        Pixel targetPixel = null;
        // loop through the columns
        for (int sourceX = 0; sourceX < this.getWidth(); sourceX++) {
            // loop through the rows
            for (int sourceY = 0; sourceY < this.getHeight(); sourceY++) {
                // set the target pixel color to the source pixel color
                sourcePixel = this.getPixel(sourceX, sourceY);
                targetPixel = target.getPixel(sourceY, this.getWidth() - 1 - sourceX);
                targetPixel.setColor(sourcePixel.getColor());
            }
        }
        return target;
    }

    public Picture rotateRight() {
        Picture target = new Picture(this.getHeight(), this.getWidth());
        Pixel sourcePixel = null;
        Pixel targetPixel = null;

        // loop through the columns
        for (int x = 0; x < this.getWidth(); x++) {
            // loop through the rows
            for (int y = 0; y < this.getHeight(); y++) {
                // set the target pixel color to the source pixel color
                sourcePixel = this.getPixel(x, y);
                targetPixel = target.getPixel(this.getHeight() - 1 - y, x);
                targetPixel.setColor(sourcePixel.getColor());
            }
        }
        return target;
    }

    public Picture rotate(int degrees) {
        // set up the rotation tranform
        AffineTransform rotateTransform = new AffineTransform();
        rotateTransform.rotate(Math.toRadians(degrees));
        Rectangle2D rect= getTransformEnclosingRect(rotateTransform);

        // create a new picture object big enough to hold the result no
        // matter what the rotation is
        Picture result = new Picture((int) (Math.ceil(rect.getWidth())),
                (int) (Math.ceil(rect.getHeight())));

        // get the graphics 2d object from the result
        Graphics graphics = result.getGraphics();
        Graphics2D g2 = (Graphics2D) graphics;

        // save the current transformation and set-up to center the 
        // rotated image
        AffineTransform savedTrans = g2.getTransform();
        AffineTransform centerTrans = new AffineTransform();
        centerTrans.translate(0 - rect.getX(), 0 - rect.getY());
        g2.setTransform(centerTrans);

        // draw the current image onto the result image rotated
        g2.drawImage(this.getImage(), rotateTransform, null);

        // reset g2 transformation to the saved one
        g2.setTransform(savedTrans);

        return result;
    }

Vertical and horizontal Reflection

This part is about reflection an image in horizontal and vertical and i did not face any problems about it because it was a obvious

The methods that we used:

    private void VerticalReflectionActionPerformed(java.awt.event.ActionEvent evt) {                                                   
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            int source_y = 0;
            int target_y = pic.getHeight() - 1;
            while (target_y > source_y) {
                for (int x = 0; x < pic.getWidth(); x++) {
                    Pixel source_Pixel = pic.getPixel(x, source_y);
                    Pixel target_Pixel = pic.getPixel(x, target_y);
                    target_Pixel.setColor(source_Pixel.getColor());
                }
                target_y--;
                source_y++;
            }
            updateIMG();
        }
      private void HorizontalReflectionActionPerformed(java.awt.event.ActionEvent evt) {                                                     
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image ", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            for (int x = 0; x < pic.getHeight(); x++) {
                int target_x = pic.getWidth() - 1;
                int source_x = 0;
                while (target_x > source_x) {
                    Pixel source_Pixel = pic.getPixel(source_x, x);
                    Pixel target_Pixel = pic.getPixel(target_x, x);
                    target_Pixel.setColor(source_Pixel.getColor());
                    target_x--;
                    source_x++;
                }
            }
            updateIMG();
        }
    }

D1 and D2 top to bottom and bottom to top reflections

This part was also straightforward and i didn't face any issues doing it it's about reflecting images with any diagonal from either bottom to top or top to bottom

The methods that we used:

   public void ReflectD1_T2B() {
        Pixel leftPixel = null;
        Pixel rightPixel = null;
        for (int y = 1; y < this.getHeight(); y++) {
            for (int x = 0; x < y; x++) {
                rightPixel = this.getPixel(x, y);
                leftPixel = this.getPixel(y, x);
                rightPixel.setColor(leftPixel.getColor());
            }
        }
    }

    public void ReflectD1_B2T() {
        Pixel leftPixel = null;
        Pixel rightPixel = null;
        for (int y = 1; y < this.getHeight(); y++) {
            for (int x = 0; x < y; x++) {
                leftPixel = this.getPixel(x, y);
                rightPixel = this.getPixel(y, x);
                rightPixel.setColor(leftPixel.getColor());
            }
        }
    }

    public void ReflectD2_T2B() {
        Pixel leftPixel = null;
        Pixel rightPixel = null;
        int height = this.getHeight();
        for (int y = height - 2; y >= 0; y--) {
            for (int x = 0; x < height - 1 - y; x++) {
                leftPixel = this.getPixel(x, y);
                rightPixel = this.getPixel(height - 1 - y, height - 1 - x);
                rightPixel.setColor(leftPixel.getColor());
            }
        }
    }

    public void ReflectD2_B2T() {
        Pixel leftPixel = null;
        Pixel rightPixel = null;
        int height = this.getHeight();
        for (int y = height - 2; y >= 0; y--) {
            for (int x = 0; x < height - 1 - y; x++) {
                rightPixel = this.getPixel(x, y);
                leftPixel = this.getPixel(height - 1 - y, height - 1 - x);
                rightPixel.setColor(leftPixel.getColor());
            }
        }
    }

Scaling down and up

Scaling down and up logic was easy but somehow we keep hitting blocks and issues on the way. i think the biggest issue the that the difference is not really shown inside the GUI because as i said earlier the GUI automatically adjusts the image by scaling it up or down to fit perfectly that's why we the showing the image on a new window. okay with that out of the way let me clarify more on the slider i know it's a bit confusing.
the slider mainly has 4 checks the users can select from (0,1) in the right side of the slider and (3,4) in the left side the right side will represent scaling down and the left for up. if the user chooses 1 it means he wants to scale it down by the biggest amount possible so we do scale it down and reset the slider to the middle in case he want to rescale it down again and the same concept goes for scaling up

The methods that we used:

	private void scaleSliderMouseReleased(java.awt.event.MouseEvent evt) {                                          
        // TODO add your handling code here:

        // Scale code here.
        if (pic != null) {
            int scaleValue = scaleSlider.getValue();
            if (scaleValue == 0) {
                pic = pic.scaleDown(3);
            } else if (scaleValue == 1) {
                pic = pic.scaleDown(2);
            } else if (scaleValue == 3) {
                pic = pic.scaleUp(2);
            } else if (scaleValue == 4) {
                pic = pic.scaleUp(3);
            }
            scaleSlider.setValue(2);
            pic.show();
            // updateIMG();
        }
    }  

    public Picture scaleUp(int numTimes) {
        Picture targetPicture = new Picture(this.getWidth() * numTimes, this.getHeight() * numTimes);
        Pixel sourcePixel = null;
        Pixel targetPixel = null;
        int targetX = 0;
        int targetY = 0;

        // loop through the source picture columns
        for (int sourceX = 0; sourceX < this.getWidth(); sourceX++) {
            // loop through the source picture rows
            for (int sourceY = 0; sourceY < this.getHeight(); sourceY++) {
                // get the source pixel
                sourcePixel = this.getPixel(sourceX, sourceY);

                // loop copying to the target y
                for (int indexY = 0; indexY < numTimes; indexY++) {
                    // loop copying to the target x
                    for (int indexX = 0; indexX < numTimes; indexX++) {
                        targetX = sourceX * numTimes + indexX;
                        targetY = sourceY * numTimes + indexY;
                        targetPixel = targetPicture.getPixel(targetX, targetY);
                        targetPixel.setColor(sourcePixel.getColor());
                    }
                }
            }
        }

        return targetPicture;

    }

    public Picture scaleDown(int numTimes) {

        Picture targetPicture = new Picture(this.getWidth(), this.getHeight());
        Pixel sourcePixel = null;
        Pixel targetPixel = null;

        // loop through the source picture columns
        for (int sourceX = 0, targetX = 0; sourceX < targetPicture.getWidth(); sourceX += numTimes, targetX++) {

            for (int sourceY = 0, targetY = 0; sourceY < targetPicture.getHeight(); sourceY += numTimes, targetY++) {

                sourcePixel = this.getPixel(sourceX, sourceY);

                targetPixel = targetPicture.getPixel(targetX, targetY);

                targetPixel.setColor(sourcePixel.getColor());

            }
        }

        return targetPicture;

    }	
	
	

Creating collage

This method is about taking 4 extra images and making a collage with the original image and then align them to the corners of the original image. the only issue i faced was when dealing with images of with different dimensions ie: height and width. it will not be shown perfectly aligned.

The methods that we used:

    private void createCollage() {
        // ask for 4 picture
        Picture second = null;
        Picture third = null;
        Picture forth = null;
        Picture fifth = null;
        JFileChooser FileChooser = new JFileChooser("");
        int conf = JOptionPane.showConfirmDialog(null, "Choose the first image", "Choose image", JOptionPane.OK_CANCEL_OPTION);
        if (conf == 0) {
            int val = FileChooser.showOpenDialog(null);
            if (val == JFileChooser.APPROVE_OPTION) {
                second = new Picture(FileChooser.getSelectedFile().getAbsolutePath());
            }
        }
        conf = JOptionPane.showConfirmDialog(null, "Choose the third image", "Choose image", JOptionPane.OK_CANCEL_OPTION);
        if (conf == 0) {
            int val = FileChooser.showOpenDialog(null);
            if (val == JFileChooser.APPROVE_OPTION) {
                third = new Picture(FileChooser.getSelectedFile().getAbsolutePath());
            }
        }
        conf = JOptionPane.showConfirmDialog(null, "Choose the forth image", "Choose image", JOptionPane.OK_CANCEL_OPTION);
        if (conf == 0) {
            int val = FileChooser.showOpenDialog(null);
            if (val == JFileChooser.APPROVE_OPTION) {
                forth = new Picture(FileChooser.getSelectedFile().getAbsolutePath());
            }
        }
        conf = JOptionPane.showConfirmDialog(null, "Choose the fifth image", "Choose image", JOptionPane.OK_CANCEL_OPTION);
        if (conf == 0) {
            int val = FileChooser.showOpenDialog(null);
            if (val == JFileChooser.APPROVE_OPTION) {
                fifth = new Picture(FileChooser.getSelectedFile().getAbsolutePath());
            }
        }

        int width = pic.getWidth() + second.getWidth() + third.getWidth();
        int height = pic.getHeight() + second.getHeight() + forth.getHeight();
        pic = new Picture(width, height);

        Pixel sourcePixel;
        Pixel targetPixel;

        // top left
        for (int i = 0; i < second.getWidth(); i++) {
            for (int j = 0; j < second.getHeight(); j++) {
                sourcePixel = second.getPixel(i, j);
                targetPixel = pic.getPixel(i, j);
                targetPixel.setColor(sourcePixel.getColor());
            }
        }
        // top right
        for (int j = 0; j < third.getWidth(); j++) {
            for (int k = 0; k < third.getHeight(); k++) {
                sourcePixel = third.getPixel(j, k);
                targetPixel = pic.getPixel((sourcePicture.getWidth() + j + second.getWidth()), (k));
                targetPixel.setColor(sourcePixel.getColor());
            }
        }
        // original picture in the middle
        for (int j = 0; j < sourcePicture.getWidth(); j++) {
            for (int k = 0; k < sourcePicture.getHeight(); k++) {
                sourcePixel = sourcePicture.getPixel(j, k);
                targetPixel = pic.getPixel((j + second.getWidth()), (k + second.getHeight()));
                targetPixel.setColor(sourcePixel.getColor());
            }
        }
        // bottom left
        for (int j = 0; j < forth.getWidth(); j++) {
            for (int k = 0; k < forth.getHeight(); k++) {
                sourcePixel = forth.getPixel(j, k);
                targetPixel = pic.getPixel((j), (k + second.getHeight() + sourcePicture.getHeight()));
                targetPixel.setColor(sourcePixel.getColor());
            }
        }
        // bottom right
        for (int j = 0; j < fifth.getWidth(); j++) {
            for (int k = 0; k < fifth.getHeight(); k++) {
                sourcePixel = fifth.getPixel(j, k);
                targetPixel = pic.getPixel((j + second.getWidth() + sourcePicture.getWidth()), (k + third.getHeight() + sourcePicture.getHeight()));
                targetPixel.setColor(sourcePixel.getColor());
            }
        }
        updateIMG();
    }

Computing and plotting histogrtams

Okay so the first method i used was to get the values of the intensity from the picture and store in the array of linked list it's a 2d array [3] for Red, Green and blue and 256 for all levels of the color after it fills the array it will call another method that will actually plot the histograms it.

    private Pixel_LL[][] ComputeHistograms() {

        Pixel_LL[][] Histograms = new Pixel_LL[3][256]; // [0] red, [1] green [2] blue

        int maxR = 0;
        int maxR_index = 0;
        int maxG = 0;
        int maxG_index = 0;
        int maxB = 0;
        int maxB_index = 0;

        for (int i = 0; i < 256; i++) { // Inisilazing all the arrays
            Histograms[0][i] = new Pixel_LL();
            Histograms[1][i] = new Pixel_LL();
            Histograms[2][i] = new Pixel_LL();
        }
        for (int i = 0; i < pic.getWidth(); i++) {
            for (int j = 0; j < pic.getHeight(); j++) {
                int intensityR = pic.getPixel(i, j).getRed();
                int intensityG = pic.getPixel(i, j).getGreen();
                int intensityB = pic.getPixel(i, j).getBlue();
                Histograms[0][intensityR].addPixel(new PixelLinkedList_node(i, j));
                Histograms[1][intensityG].addPixel(new PixelLinkedList_node(i, j));
                Histograms[2][intensityB].addPixel(new PixelLinkedList_node(i, j));

                if (Histograms[0][intensityR].getTotal() > maxR) {
                    maxR = Histograms[0][intensityR].getTotal();
                    maxR_index = intensityR;
                }
                if (Histograms[1][intensityG].getTotal() > maxG) {
                    maxG = Histograms[1][intensityG].getTotal();
                    maxG_index = intensityG;
                }
                if (Histograms[2][intensityB].getTotal() > maxB) {
                    maxB = Histograms[2][intensityB].getTotal();
                    maxB_index = intensityB;
                }
            }
        }
        // ***plotting the histogrmas***
        if ((maxR == maxG && maxR == maxB)) {// image is gray
            if ((maxR_index == maxG_index && maxG_index == maxB_index)) {
                PlotHistogram("Gray", Histograms[0]);
                return Histograms;
            }
        }
        PlotHistogram("RED", Histograms[0]);
        PlotHistogram("GREEN", Histograms[1]);
        PlotHistogram("BLUE", Histograms[2]);
        return Histograms;
    }

The following method is resonsible for plotting the acutal histogram it will simply make a picture and draw it pixel by pixel

    private void PlotHistogram(String color, Pixel_LL Histogram[]) {
        // first must find the max height of the histogram.
        int maxHeight = 0;
        for (int i = 0; i < Histogram.length; i++) {
            if (Histogram[i].getTotal() > maxHeight) {
                maxHeight = Histogram[i].getTotal();
            }
        }
        
        Picture histogram = new Picture(256, 256, Color.white);

        Color c;
        if (color.equalsIgnoreCase("RED")) {
            c = Color.RED;
        } else if (color.equalsIgnoreCase("green")) {
            c = Color.GREEN;
        } else if (color.equalsIgnoreCase("BLue")) {
            c = Color.BLUE;
        } else {
            c = Color.GRAY;
        }
        
        // Actual max = maxHeight
        // the max we want to make is 256 so it become visible
        for (int i = 0; i < 256; i++) { // now plot the histogram
            int max = (int) (Histogram[i].getTotal() * 256 / maxHeight);
            for (int j = 255; j >= (256 - max); j--) {
                histogram.getPixel(i, j).setColor(c);
            }
        }
        histogram.scaleUp(2).show();
    }

The following method for exporting the histograms to a txt file for storage, most of actual printing for each pixel is done inside the linked list manager class in this method i only sent a refrence of the printwriter

    private void exportHistogramActionPerformed(java.awt.event.ActionEvent evt) {                                                

        // exporting the histogram to files
        Pixel_LL[][] Histograms = ComputeHistograms();
        // ask the user where to save it
        JFileChooser f = new JFileChooser();
        f.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        f.showSaveDialog(null);
        File export = new File(f.getSelectedFile() + "\\histograms.txt");

        PrintWriter pen = null;
        try {
            pen = new PrintWriter(export);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(PictureEditor.class.getName()).log(Level.SEVERE, null, ex);
        }
        pen.println(pic.getWidth() + " " + pic.getHeight());
        pen.println("RED");
        for (int i = 0; i < 256; i++) {
            Histograms[0][i].exportHistogram(pen);
        }
        pen.println("GREEN");
        for (int i = 0; i < 256; i++) {
            Histograms[1][i].exportHistogram(pen);
        }
        pen.println("BLUE");
        for (int i = 0; i < 256; i++) {
            Histograms[2][i].exportHistogram(pen);
        }
        pen.close();
        JOptionPane.showMessageDialog(null, "Finished\n Exported to " + export.getAbsolutePath(), "Done", JOptionPane.INFORMATION_MESSAGE);
    }

The following method is where the magic happens, it's for importing the histograms from a txt file and plotting the picture again and the histograms also, it's like the import method but instead of writing it reads the data firsts and then it starts to redraw the image.
i think it can be improved by starting to draw the pixel is soon as you read it from the file not after reading all the file. they are seperate now because i did the second part first then got the idea of exporting the pixels to a file.

    private void importHistogramActionPerformed(java.awt.event.ActionEvent evt) {                                                
        try {
            // TODO add your handling code here:

            JOptionPane.showMessageDialog(null, "Please choose the histogram txt", "File", JOptionPane.OK_OPTION);

            JFileChooser f = new JFileChooser();
            f.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
            f.showSaveDialog(null);
            File export = f.getSelectedFile();

            Scanner input = new Scanner(export);
            Picture replot = new Picture(input.nextInt(), input.nextInt());

            Pixel_LL[] HistogramsRed = new Pixel_LL[256];
            Pixel_LL[] HistogramsGreen = new Pixel_LL[256];
            Pixel_LL[] HistogramsBlue = new Pixel_LL[256];
            int total;
            String color = input.next();
            System.out.println("Importing the color " + color);
            for (int i = 0; i < 256; i++) { // Reading all level 0 pixels from red
                total = input.nextInt();
                System.out.println("Importing level " + i + " of " + color + " it has: " + total + " pixels");
                HistogramsRed[i] = new Pixel_LL(input, total);
            }
            color = input.next();
            System.out.println("Importing the color " + color);
            for (int i = 0; i < 256; i++) { // Reading all level 0 pixels from green
                total = input.nextInt();
                System.out.println("Importing level " + i + " of " + color + " it has: " + total + " pixels");
                HistogramsGreen[i] = new Pixel_LL(input, total);
            }
            color = input.next();
            System.out.println("Importing the color " + color);
            for (int i = 0; i < 256; i++) { // Reading all level 0 pixels from blue
                total = input.nextInt();
                System.out.println("Importing level " + i + " of " + color + " it has: " + total + " pixels");
                HistogramsBlue[i] = new Pixel_LL(input, total);
            }
            PixelLinkedList_node helpPtr = null;

            for (int i = 0; i < 256; i++) { // 0 - 256
                if (HistogramsRed[i].getHead() != null) {
                    helpPtr = HistogramsRed[i].getHead();
                    while (helpPtr != null) {
                        replot.getPixel(helpPtr.getX(), helpPtr.getY()).setRed(i);
                        helpPtr = helpPtr.getNext();
                    }
                }

                if (HistogramsGreen[i].getHead() != null) {
                    helpPtr = HistogramsGreen[i].getHead();
                    while (helpPtr != null) {
                        replot.getPixel(helpPtr.getX(), helpPtr.getY()).setGreen(i);
                        helpPtr = helpPtr.getNext();
                    }
                }

                if (HistogramsBlue[i].getHead() != null) {
                    helpPtr = HistogramsBlue[i].getHead();
                    while (helpPtr != null) {
                        replot.getPixel(helpPtr.getX(), helpPtr.getY()).setBlue(i);
                        helpPtr = helpPtr.getNext();
                    }
                }
            }
            System.out.println("DONE");
            replot.show();
        } catch (FileNotFoundException ex) {
            JOptionPane.showMessageDialog(null, "ERROR FILE IS NOT FOUND", "File", JOptionPane.OK_OPTION);
        } catch (Exception x) {
            JOptionPane.showMessageDialog(null, "ERROR", "EROR", JOptionPane.OK_OPTION);
        }

    } 

Computing brightness

Computing Brightness that part is about counting the Brightness by using a formal that will give you the numbr of the Brightness so there is no confusion


The methods that we used: 

    private void computeBrightnessActionPerformed(java.awt.event.ActionEvent evt) {                                                  

        Pixel[] pixelArray = pic.getPixels();

        double PixelsInstenses = 0;
        double brightness = 0.0;

        // loop through all the pixels
        for (Pixel currntPixel : pixelArray) {
            PixelsInstenses += currntPixel.getAverage();

        }
        brightness = (PixelsInstenses / pixelArray.length) / 255;

        JOptionPane.showMessageDialog(null, "Number of brightness = " + brightness);
    }  

Computing contrast

In this method will compute contrast first we will get all pixels for picture and store it in array then will make loop to go to through pixels after that we get the average color for all pixels and make 2 if statements 1- if the average > max value we will store the value in a max 2- if average < min value we will start value in min then we will use contrast formula ((max_val - min_val) / (max_val + min_val)).

The methods that we used: 

    private void computeContrastActionPerformed(java.awt.event.ActionEvent evt) {                                                
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image ", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            Pixel[] pixels = pic.getPixels();

            int Contrast;
            double max_val = 0;
            double min_val = Integer.MAX_VALUE;

            for (int i = 0; i < pixels.length; i++) {
                // Here we Devide by 3 because get the avg .
                Contrast = (int) ((pixels[i].getRed() + pixels[i].getGreen() + pixels[i].getBlue()) / 3);
                //---if Contrast Greater than max we will save the value in max-----
                if (Contrast > max_val) {
                    max_val = Contrast;
                }
                //---if Contrast less than min we will save the value in min-----
                if (Contrast < min_val) {
                    min_val = Contrast;
                }
            }
            double contrast = ((max_val - min_val) / (max_val + min_val));
            JOptionPane.showMessageDialog(null, "The level of Contrast is = " + contrast);
        }
    }

 This method was kind of complicated and several students in the group tried to solve it, but the solution was difficult to find.

Box filter

In box filter we ask the user to put an odd number that will be the box size, in box size filter we sum the values of each color from the pixels in the box range and get the average and reassign the value to the pixels.

The methods that we used: 

    private void boxFilterActionPerformed(java.awt.event.ActionEvent evt) {                                          
        // TODO add your handling code here:
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image ", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            String size = JOptionPane.showInputDialog(null, "Please Enter the size of the filter please (one odd number only): ");
            try {
                int FilterSize = Integer.parseInt(size);
                if (FilterSize % 2 == 0) {
                    JOptionPane.showMessageDialog(null, "Fitler size should be odd", "Error", JOptionPane.ERROR_MESSAGE);
                    return;
                }
                pic.BoxFilter(FilterSize);
                updateIMG();
            } catch (Exception e) {
                JOptionPane.showMessageDialog(null, "Please put integer only", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }  
    public void BoxFilter(int FilterSize) {
        if (FilterSize % 2 == 0) { // if number is even
            return;
        }

        int sumR, sumG, sumB;

        int start = (int) Math.floor(FilterSize / 2);

        for (int i = start; i <= this.getWidth() - (start + 1); i++) {
            for (int j = start; j <= this.getHeight() - (start + 1); j++) {
                sumR = 0;
                sumG = 0;
                sumB = 0;
                for (int k = -start; k <= start; k++) {
                    for (int l = -start; l <= start; l++) {
                        sumR += this.getPixel(i + k, j + l).getRed();
                        sumG += this.getPixel(i + k, j + l).getGreen();
                        sumB += this.getPixel(i + k, j + l).getBlue();
                    }
                }
                sumR /= Math.round(FilterSize * FilterSize);
                sumG /= Math.round(FilterSize * FilterSize);
                sumB /= Math.round(FilterSize * FilterSize);
                this.getPixel(i, j).setRed(sumR);
                this.getPixel(i, j).setGreen(sumG);
                this.getPixel(i, j).setBlue(sumB);
            }
        }
    }

Gaussian filter

In gassuan filter we ask the user to indicate the importance of the middle pixel in the 3 by 3 filter then just like box filter we go through all the pixels in the filter range but instead of summing and then taking the average here we multiplied the color value by the portion from the filter array, the issues i faced here is trying to make it generalized so it can take the filter size And the middle pixel priority from the user but i couldn't achieve that. 

The methods that we used: 

    private void GaussianFilterActionPerformed(java.awt.event.ActionEvent evt) {                                               
        // TODO add your handling code here:
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image ", "Error", JOptionPane.ERROR_MESSAGE);
        } else {

            // give user 3 option high middle low.
            Object[] options = {"LOW",
                "MIDDLE",
                "HIGH"};
            int choice = JOptionPane.showOptionDialog(this, "Please choose the important of the middle pixel", "Gaussuan filter option", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
            pic.gaussianFilter3x3(choice);
            System.out.println(choice);
            updateIMG();
        }
    }    
    public void gaussianFilter3x3(int choice) {
        double[][] filter = null;
        if (choice == 0) {
            filter = new double[][]{
                {0.075, 0.125, 0.075},
                {0.125, 0.200, 0.125},
                {0.075, 0.125, 0.075}
            };
        } else if (choice == 1) {
            filter = new double[][]{
                {0.05, 0.075, 0.05},
                {0.075, 0.500, 0.075},
                {0.05, 0.075, 0.05}
            };
        } else if (choice == 2) {
            filter = new double[][]{
                {0.02, 0.03, 0.02},
                {0.03, 0.800, 0.03},
                {0.02, 0.03, 0.02}
            };
        }

        int sumR, sumG, sumB;

        for (int i = 1; i <= this.getWidth() - 2; i++) {
            for (int j = 1; j <= this.getHeight() - 2; j++) {
                sumR = 0;
                sumG = 0;
                sumB = 0;
                for (int k = -1; k <= 1; k++) {
                    for (int l = -1; l <= 1; l++) {
                        sumR += filter[k + 1][l + 1] * this.getPixel(i + k, j + l).getRed();
                        sumG += filter[k + 1][l + 1] * this.getPixel(i + k, j + l).getGreen();
                        sumB += filter[k + 1][l + 1] * this.getPixel(i + k, j + l).getBlue();
                    }
                }
                this.getPixel(i, j).setRed(sumR);
                this.getPixel(i, j).setGreen(sumG);
                this.getPixel(i, j).setBlue(sumB);
            }
        }

    }

Laplacian filter

Laplacian Filter in that filter was a important filter with shows you the edge in the image , the issue that i face in this filter is how to generalize it and the matrex it should the some of all of it be zero.

The methods that we used: 

    public void LaplacianFilter(int FilterSize) {
        if (FilterSize % 2 == 0) { // if number is even
            return;
        }

        int sumR, sumG, sumB;

        double filter[][] = {{-0.075, -0.125, -0.075},
        {-0.125, 2.0, -0.125},
        {-0.075, -0.125, -0.075}};

        int start = (int) Math.floor(FilterSize / 2);

        for (int i = start; i <= this.getWidth() - (start + 1); i++) {
            for (int j = start; j <= this.getHeight() - (start + 1); j++) {
                sumR = 0;
                sumG = 0;
                sumB = 0;
                for (int k = -start; k <= start; k++) {
                    for (int l = -start; l <= start; l++) {
                        double c = filter[k + 1][l + 1];
                        int red = this.getPixel(i + k, j + 1).getRed();
                        sumR += red * c;
                        int green = this.getPixel(i + k, j + 1).getGreen();
                        sumG += green * c;
                        int blue = this.getPixel(i + k, j + 1).getBlue();
                        sumB += blue * c;
                    }
                }
                int Red = (int) Math.min(FilterSize, Math.max(0, sumR));
                int Green = (int) Math.min(FilterSize, Math.max(0, sumG));
                int Blue = (int) Math.min(FilterSize, Math.max(0, sumB));

                this.getPixel(i, j).setBlue(sumB);
                this.getPixel(i, j).setGreen(sumG);
                this.getPixel(i, j).setRed(sumR);
            }
        }
    }

Min and Max filter

the first method is about getting the minimum filter of the colors of a picture. While the maximum filter method will filter the colors to the maximum. Both methods take the filter size from the user (it has to be an odd value)

The methods that we used: 

    public void MinFilter(int FilterSize) {
        if (FilterSize % 2 == 0) { // if number is even
            return;
        }
        int Red[] = new int[FilterSize * FilterSize];
        int Green[] = new int[FilterSize * FilterSize];
        int Blue[] = new int[FilterSize * FilterSize];
        int x = this.getWidth();
        int y = this.getHeight();
        Picture copy = new Picture(x, y);
        copy.copy(this, 0, 0, x, y, 0, 0);

        int start = (int) Math.floor(FilterSize / 2);

        for (int v = start; v <= y - (start + 1); v++) {
            for (int u = start; u <= x - (start + 1); u++) {
                int k = 0;
                for (int j = -start; j <= start; j++) {
                    for (int i = -start; i <= start; i++) {

                        Red[k] = copy.getPixel(u + i, v + j).getRed();
                        Green[k] = copy.getPixel(u + i, v + j).getGreen();
                        Blue[k] = copy.getPixel(u + i, v + j).getBlue();
                        k++;
                    }
                }
                Arrays.sort(Red);
                Arrays.sort(Green);
                Arrays.sort(Blue);
                this.getPixel(u, v).setColor(new Color(Red[0], Green[0], Blue[0]));
            }
        }
    }
    public void MaxFilter(int FilterSize) {
        if (FilterSize % 2 == 0) { // if number is even
            return;
        }
        int Red[] = new int[FilterSize * FilterSize];
        int Green[] = new int[FilterSize * FilterSize];
        int Blue[] = new int[FilterSize * FilterSize];

        int x = this.getWidth();
        int y = this.getHeight();

        Picture copy = new Picture(x, y);
        copy.copy(this, 0, 0, x, y, 0, 0);

        int start = (int) Math.floor(FilterSize / 2);
        // the beggining of 
        for (int v = start; v <= y - (start + 1); v++) {
            for (int u = start; u <= x - (start + 1); u++) {
                int k = 0;
                for (int j = -start; j <= start; j++) {
                    for (int i = -start; i <= start; i++) {

                        Red[k] = copy.getPixel(u + i, v + j).getRed();
                        Green[k] = copy.getPixel(u + i, v + j).getGreen();
                        Blue[k] = copy.getPixel(u + i, v + j).getBlue();
                        k++;
                    }
                }
                Arrays.sort(Red);
                Arrays.sort(Green);
                Arrays.sort(Blue);
                this.getPixel(u, v).setColor(new Color(Red[FilterSize * FilterSize - 1], Green[FilterSize * FilterSize - 1], Blue[FilterSize * FilterSize - 1]));
            }
        }
    }

Median and weighted median filter

Median filter is a filter about removing noise form an image which that is so helpful and the difficulty was also about how to generalize the code.
The weighted median is from its name it adds weight to each pixel depending on a weight array. the difficult part here was generalizing filter size and also weighted array the way that the weight array is done is completely random and the user can't control it he can only specify the filter size.

The methods that we used: 

    public void MedianFilter(int FilterSize) {

        if (FilterSize % 2 == 0) { // if number is even
            return;
        }
        int Red[] = new int[FilterSize * FilterSize];
        int Green[] = new int[FilterSize * FilterSize];
        int Blue[] = new int[FilterSize * FilterSize];

        int start = (int) Math.floor(FilterSize / 2);

        for (int j = start; j <= this.getHeight() - (start + 1); j++) {
            for (int i = start; i <= this.getWidth() - (start + 1); i++) {
                int x = 0;
                for (int k = -start; k <= start; k++) {
                    for (int l = -start; l <= start; l++) {
                        Red[x] = this.getPixel(k + i, l + j).getRed();
                        Green[x] = this.getPixel(k + i, l + j).getGreen();
                        Blue[x] = this.getPixel(k + i, l + j).getBlue();
                        x++;
                    }
                }
                Arrays.sort(Red);
                Arrays.sort(Green);
                Arrays.sort(Blue);
                this.getPixel(i, j).setColor(new Color(Red[FilterSize * FilterSize / 2], Green[FilterSize * FilterSize / 2], Blue[FilterSize * FilterSize / 2]));
            }
        }
    }

    public void MedianWeightedFilter(int FilterSize) {
        if (FilterSize % 2 == 0) { // if number is even
            return;
        }

        // generating random weighted matrix
        int[][] WeightedMatrix = new int[FilterSize][FilterSize];
        for (int i = 0; i < FilterSize; i++) {
            for (int j = 0; j < FilterSize; j++) {
                WeightedMatrix[i][j] = 1 + (int) (Math.random() * 5); // 1 to 5
                System.out.print(WeightedMatrix[i][j] + ", ");
            }
        }

        int start = (int) Math.floor(FilterSize / 2);

        for (int i = start; i <= this.getWidth() - (start + 1); i++) {
            for (int j = start; j <= this.getHeight() - (start + 1); j++) {
                ArrayList<Integer> red = new ArrayList<>();
                ArrayList<Integer> green = new ArrayList<>();
                ArrayList<Integer> blue = new ArrayList<>();
                int x = 0;
                int x2 = 0;

                for (int k = -start; k <= start; k++) {
                    for (int l = -start; l <= start; l++) {

                        for (int m = 0; m < WeightedMatrix[x][x2]; m++) {
                            red.add(this.getPixel(k + i, l + j).getRed());
                            green.add(this.getPixel(k + i, l + j).getGreen());
                            blue.add(this.getPixel(k + i, l + j).getBlue());
                        }
                    }
                    x++;
                    x2++;
                }
                Collections.sort(red);
                Collections.sort(green);
                Collections.sort(blue);
                int middle = FilterSize * FilterSize / 2;

                if (red.size() % 2 == 0) { // array size is even - get avg of two middle pixels
                    int avgR = (red.get(middle) + red.get(middle - 1)) / 2;
                    int avgG = (green.get(middle) + green.get(middle - 1)) / 2;
                    int avgB = (blue.get(middle) + blue.get(middle - 1)) / 2;
                    this.getPixel(i, j).setColor(new Color(avgR, avgG, avgB));
                } else {
                    this.getPixel(i, j).setColor(new Color(red.get(middle), green.get(middle), blue.get(middle)));
                }
            }
        }
    }

Red eye reduction

In this method it's about changing the red in the eye so first we need to know the range of the eye so User will specify top left and bottom right corners of rectangular region, and then we also need the threshold value and also the new color that the user want the eye to be. the issue was trying to make all of that automatic.

The methods that we used: 

    private void redeyeActionPerformed(java.awt.event.ActionEvent evt) {                                       
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image please!", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            JFrame parent = new JFrame();
            JOptionPane.showMessageDialog(parent, "Please click on two points in the image");

            imgLabel.addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    System.out.println("Clicked!");
                    System.out.println(e.getX());
                    System.out.println(e.getY());
                    numOfClicks++;
                    if (numOfClicks == 1) {
                        x1 = e.getX();
                        y1 = e.getY();
                    } else if (numOfClicks == 2) {
                        x2 = e.getX();
                        y2 = e.getY();

                        double W = (pic.getWidth() * 1.00 / imgLabel.getWidth());
                        double H = (pic.getHeight() * 1.00 / imgLabel.getHeight());

                        x1 = (int) (W * x1);
                        x2 = (int) (W * x2);
                        y1 = (int) (H * y1);
                        y2 = (int) (H * y2);

                        Color newColor = JColorChooser.showDialog(null, "Choose New Color", Color.BLACK);
                        int threshold = Integer.parseInt(JOptionPane.showInputDialog("Threshold:"));
                        for (int i = y1; i < y2; i++) {
                            for (int j = x1; j < x2; j++) {
                                Pixel p = pic.getPixel(j, i);
                                //here we compare because get the different between Red color amd pxl .
                                if (p.colorDistance(Color.RED) < threshold) {
                                    p.setColor(newColor);
                                }
                            }
                        }
                        updateIMG();
                        numOfClicks = 0;
                        imgLabel.removeMouseListener(this);
                    }
                }
            });
        }

    }   

Background subtraction

This method is about removing and old background and replacing it with a new one.

The methods that we used: 

    private void BackgroundActionPerformed(java.awt.event.ActionEvent evt) {                                           

        if (pic != null) {
            if (Threshold1.getValue() > 0 && Threshold2.getValue() > 0) {
                JFileChooser FileChooser = new JFileChooser("C:\\Users\\alish\\Downloads\\SoMultimedia");
                int conf = JOptionPane.showConfirmDialog(null, "Choose old background image", "Choose Old Background", JOptionPane.OK_CANCEL_OPTION);
                if (conf == 0) {
                    int val = FileChooser.showOpenDialog(null);
                    if (val == JFileChooser.APPROVE_OPTION) {
                        Picture oldBackground = new Picture(FileChooser.getSelectedFile().getAbsolutePath());
                        conf = JOptionPane.showConfirmDialog(null, "Choose new background image", "Choose New Background", JOptionPane.OK_CANCEL_OPTION);
                        if (conf == 0) {
                            val = FileChooser.showOpenDialog(null);
                            if (val == JFileChooser.APPROVE_OPTION) {

                                Picture newBackground = new Picture(FileChooser.getSelectedFile().getAbsolutePath());
                                if (pic.getHeight() <= newBackground.getHeight() && pic.getWidth() <= newBackground.getWidth()) {
                                    int[] values = new int[2];
                                    values[0] = Threshold1.getValue();
                                    values[1] = Threshold2.getValue();
                                    Background_Subtraction(values, oldBackground, newBackground);
                                } else {
                                    JOptionPane.showMessageDialog(null, "New Background Size must be >= Picture Size", "Error", JOptionPane.ERROR_MESSAGE);

                                }

                            }
                        }
                    }
                }
            } else {
                JOptionPane.showMessageDialog(null, "Please adjust the threshold > 0", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }   

    private void Background_Subtraction(int[] values, Picture oldBackground, Picture newBackground) {
        Pixel picPixel = null;
        Pixel oldPixel = null;
        Pixel newPixel = null;
        multimedia.Grid grid = new multimedia.Grid();
        grid.setTargts(pic); // as the original pic as first target.
        for (int i = 0; i < values.length; i++) {
            Picture copyPic = new Picture(pic);//take a copy of the original pic
            for (int x = 0; x < copyPic.getWidth(); x++) {
                for (int y = 0; y < copyPic.getHeight(); y++) {
                    picPixel = copyPic.getPixel(x, y);
                    oldPixel = oldBackground.getPixel(x, y);
                    newPixel = newBackground.getPixel(x, y);
                    //compare the orignal image with the old background
                    if (picPixel.colorDistance(oldPixel.getColor()) < values[i]) {
                        picPixel.setColor(newPixel.getColor());
                    }
                }
            }
            grid.setTargts(copyPic);//add the image to the grid
        }
        grid.setVisible(true);
        grid.DisplayEdges();
    }

Chromacky subtracion

in this method we ask the user to put a new background it has be with the same width and height of the original picture and then add the pixels from the new background in the places where there is a chroma, here it's green it can be changed to any color.

The methods that we used: 

     private void ChromackyActionPerformed(java.awt.event.ActionEvent evt) {                                          
        if (pic != null) {
            JFileChooser FileChooser = new JFileChooser("C:\\Users\\alish\\Downloads\\SoMultimedia");
            int conf = JOptionPane.showConfirmDialog(null, "Choose new background image", "Choose New Background", JOptionPane.OK_CANCEL_OPTION);
            if (conf == 0) {
                int val = FileChooser.showOpenDialog(null);
                if (val == JFileChooser.APPROVE_OPTION) {
                    Picture newBackground = new Picture(FileChooser.getSelectedFile().getAbsolutePath());
                    if (newBackground.getHeight() >= pic.getHeight() && newBackground.getWidth() >= pic.getWidth()) {
                        Chromacky(newBackground);
                    } else {
                        JOptionPane.showMessageDialog(null, "New Background Size must be >= Picture Size", "Error", JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
        } else {
            JOptionPane.showMessageDialog(null, "Select an image please!", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }  
   private void Chromacky(Picture newBackground) {
        Pixel picPixel = null;
        Pixel newPixel = null;
        for (int i = 0; i < pic.getWidth(); i++) {
            for (int j = 0; j < pic.getHeight(); j++) {
                picPixel = pic.getPixel(i, j);
                newPixel = newBackground.getPixel(i, j);
                if (picPixel.colorDistance(Color.GREEN) < 160) {
                    picPixel.setColor(newPixel.getColor());
                }
            }
        }
        updateIMG();
    }

Edge detection

in edge detection method there is two part the first is doing it with normal aproach without the help of any libraries, the second the one is using OpenCV[4] library.

The methods that we used: 

    // new edgeDetection method that takes left and right pixels with top and bottom.
    public void edgeDetection_LR(int amount) {
        Pixel topPixel = null;
        Pixel bottomPixel = null;
        Pixel leftPixel = null;
        Pixel rightPixel = null;
        double topAverage = 0.0;
        double bottomAverage = 0.0;
        double rightAverage = 0.0;
        double leftAverage = 0.0;

        int endY = this.getHeight() - 1;

        /* loop through y values from 0 to height - 1
         * (since compare to below pixel) */
        for (int y = 0; y < endY; y++) {

            // loop through the x values from 0 to width
            for (int x = 1; x < this.getWidth() - 1; x++) {

                // get the top and bottom pixels
                topPixel = this.getPixel(x, y);
                bottomPixel = this.getPixel(x, y + 1);
                leftPixel = this.getPixel(x - 1, y);
                rightPixel = this.getPixel(x + 1, y);

                // get the color averages for the two pixels
                topAverage = topPixel.getAverage();
                bottomAverage = bottomPixel.getAverage();
                leftAverage = leftPixel.getAverage();
                rightAverage = rightPixel.getAverage();

                /* check if the absolute value of the difference
                 * is less than the amount */
                if ((Math.abs(topAverage - bottomAverage) < amount) || (Math.abs(rightAverage - leftAverage) < amount)) {
                    topPixel.setColor(Color.WHITE);
                    // else set the color to black
                } else { // edge is detected.
                    topPixel.setColor(Color.BLACK);
                }
            }
        }
    }

    private void edgeDetecLapActionPerformed(java.awt.event.ActionEvent evt) {                                             
        // TODO add your handling code here:
        if (pic == null) {
            JOptionPane.showMessageDialog(null, "Select an image please!", "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            // edge detection using laplactian filter from openCV library
            // Declare the variables we are going to use
            Mat src, src_gray = new Mat(), dst = new Mat();
            int kernel_size = 3;
            int scale = 1;
            int delta = 0;
            int ddepth = CvType.CV_16S;
            src = Imgcodecs.imread(imgName, Imgcodecs.IMREAD_COLOR); // Load an image
            if (src.empty()) {
                System.out.println("Error opening image");
                return;
            }
            // Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
            Imgproc.GaussianBlur(src, src, new Size(3, 3), 0, 0, Core.BORDER_DEFAULT);
            // Convert the image to grayscale
            Imgproc.cvtColor(src, src_gray, Imgproc.COLOR_RGB2GRAY);
            Mat abs_dst = new Mat(); // the image
            Imgproc.Laplacian(src_gray, dst, ddepth, kernel_size, scale, delta, Core.BORDER_DEFAULT);
            // converting back to CV_8U
            Core.convertScaleAbs(dst, abs_dst);
            HighGui.imshow("Edge using laplacian filter", abs_dst);
            HighGui.waitKey(1000);
        }
    }  

Change volume

this method is simple, we use a slider to control the volume, the value of the slider will be divided with 50 then it will be passed through the changeVolume method which is there in the sound class[5].

The method that we used:

    private void volumeSliderMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_volumeSliderMouseReleased
        // TODO add your handling code here:

        currentSound.changeVolume(volumeSlider.getValue() / 50);
        currentSound.play();

    }//GEN-LAST:event_volumeSliderMouseReleased

    /**
     * Method to modify the volume by the passed factor in the passed range
     *
     * @param factor the amount to multiply the value by
     * @param start the index to use as the start (inclusive)
     * @param end the index to end at (exclusive)
     */
    public void changeVolume(double factor,
            int start,
            int end) {
        int value = 0;
        for (int i = start;
                i < this.getLength() && i < end;
                i++) {
            value = this.getSampleValueAt(i);
            this.setSampleValueAt(i, (int) (value * factor));
        }
    }

Clipping sounds

this method will take two inputs from the user and it will clip the sound according to the first value, which will be the beginning index, and the last index is set by the second value, these value it will be passed through the Clipping method.

The method that we used:

    private void clipSoundActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clipSoundActionPerformed

        String first = JOptionPane.showInputDialog(null, "Enter the start Index: ");
        String last = JOptionPane.showInputDialog(null, "Enter the end Index: ");
        int firstIndx = Integer.parseInt(first);
        int lastIndx = Integer.parseInt(last);

        Sound s = currentSound.clip(firstIndx, lastIndx);
        s.play();

    }//GEN-LAST:event_clipSoundActionPerformed


    /**
     * Method to create a new sound by copying just part of the current sound to a new sound
     *
     * @param start the index to start the copy at (inclusive)
     * @param end the index to stop the copy at (inclusive)
     * @return a new sound with just the samples from start to end in it
     */
    public Sound clip(int start, int end) {
        // calculate the number of samples in the clip
        int lengthInSamples = end - start + 1;
        Sound target = new Sound(lengthInSamples); // hold clip
        int value = 0;        // holds the current sample value
        int targetIndex = 0;  // index in target sound

        // copy from start to end from source into target
        for (int i = start; i <= end; i++, targetIndex++) {
            value = this.getSampleValueAt(i);
            target.setSampleValueAt(targetIndex, value);
        }

        return target;
    }

Splicing

In this method we take multiple audio files as many as the user wants, and adding them togther and leaving a silent zone in between each two. the issue that i faced was after splicing the sound become slow and heavy i have no idea why is that.

The method that we used:

    private void splicingSoundActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_splicingSoundActionPerformed
        // TODO add your handling code here:
        currentSound = currentSound.splice(1);
        while (true) {
            int reply = JOptionPane.showConfirmDialog(null, "Do you want to splice another sound ?", "Splice?", JOptionPane.YES_NO_OPTION);
            if (reply == JOptionPane.YES_OPTION) {
                currentSound = currentSound.splice(1);
            } else {
                currentSound.play();
                break;
            }
        }
    }//GEN-LAST:event_splicingSoundActionPerformed

    public Sound splice(double zone) {
        Sound sound1 = new Sound(this);
        Sound sound2 = new Sound(FileChooser.pickAFile());

        int targetIndex = 0; // the starting place on the target

        Sound s = new Sound((this.getLength() + sound2.getLength()) + (int) (this.getSamplingRate() * (Math.ceil(zone))));

        // copy all of sound 1 into the current sound (target)
        for (int i = 0; i < sound1.getLength(); i++, targetIndex++) {
            s.setSampleValueAt(targetIndex, this.getSampleValueAt(i));
        }

        // create silence between words by setting values to 0
        for (int i = 0; i < (int) (this.getSamplingRate() * zone); i++, targetIndex++) {
            s.setSampleValueAt(targetIndex, 0);
        }

        // copy all of sound 2 into the current sound (target)
        for (int i = 0; i < sound2.getLength(); i++, targetIndex++) {
            s.setSampleValueAt(targetIndex, sound2.getSampleValueAt(i));
        }
        return s;
    }

Spread then squeeze

this method is kind-of weird and i maybe didn't understant what is really requerid, however what i did was first spreading the audio file by the double and then squeeze that spreaded file to /4.

The method that we used:

    public Sound spreadThenSqueeze() {
        SoundSample[] sample = this.getSamples();

        // Spread * 2
        Sound spreaded = new Sound((int) sample.length * 2);
        for (double sourceIndex = 0, targetIndex = 0; targetIndex < spreaded.getLength(); sourceIndex += 0.5, targetIndex++) {
            spreaded.setSampleValueAt((int) targetIndex, sample[((int) sourceIndex)].getValue());
        }

        // Squeeze / 4
        SoundSample[] sampleSpreaded = spreaded.getSamples();
        Sound squeezed = new Sound((int) sampleSpreaded.length / 4);
        for (double sourceIndex = 0, targetIndex = 0; targetIndex < squeezed.getLength(); sourceIndex += 2, targetIndex++) {
            squeezed.setSampleValueAt((int) targetIndex, sampleSpreaded[((int) sourceIndex)].getValue());
        }

        return squeezed;
    }

Reversing

Reversing the sound method is an easy part there is nothing difficult about it 

The method that we used:

    private void reversingSoundActionPerformed(java.awt.event.ActionEvent evt) {

        SoundSample[] sound = currentSound.getSamples();
        int index = 0;

        for (int i = currentSound.getLength() - 1; i >= 0; i--) {

            SoundSample temp = sound[i];
            sound[index] = temp;

            currentSound.setSampleValueAt(index, currentSound.getSampleValueAt(i));
            index++;
        }

        currentSound.play();

    }

Blending

blending sounds is a method about playing two sound at the same time, the issue about is i understan it in a wrong why i thought that i should two separated sounds

The method that we used:

    private void blendingSoundActionPerformed(java.awt.event.ActionEvent evt) {                                              

    
        String numberOfSound = JOptionPane.showInputDialog(null, "Please Enter the number of sounds : ");
        int num = Integer.parseInt(numberOfSound);

        if (num==2) {
            
       String filePath1 = FileChooser.pickAFile();
       Sound sound1 = new Sound(filePath1);
       
        for (int i = 0; i < currentSound.getLength() - 1 ; i++) {
            
            currentSound.setSampleValueAt(i , currentSound.getSampleValueAt(i) + sound1.getSampleValueAt(i));
                 
        }
        }else if (num==3) {
            String filePath1 = FileChooser.pickAFile();
        Sound sound1 = new Sound(filePath1);
        String filePath2 = FileChooser.pickAFile();
        Sound sound2 = new Sound(filePath2);
        
        for (int i = 0; i < currentSound.getLength() - 1 ; i++) {
            
            currentSound.setSampleValueAt(i , currentSound.getSampleValueAt(i) + sound1.getSampleValueAt(i)+sound2.getSampleValueAt(i));
                 
        }
        }
        
        
         currentSound.play();
    }       

Simple and weighted average filter

this method is about applying a simple average filter to the sound, we ask the user to give us the filters size the go through all the sample sizes and take the average of filter size for each sample index and assign the output to the new sound. the issue is I'm not hearing any difference in the sound i have tried with large and small filter sizes.

The method that we used:

    private void simpleAVGActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_simpleAVGActionPerformed
        // TODO add your handling code here:
        // take num
        String size = JOptionPane.showInputDialog(null, "Please Enter the size of window please: ");
        try {
            int Size = Integer.parseInt(size);
            currentSound.simpleAverage(Size);
            currentSound.play();
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(null, "Please put integer only", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_simpleAVGActionPerformed


    public void simpleAverage(int windowsSize) {

        SoundSample[] sampleArray = this.getSamples();
        System.out.println(sampleArray.length);

        Sound newSound = new Sound(this.getSamples().length);

        int start = (int) Math.floor(windowsSize / 2);
        int avg = 0;

        // the start index where we can't apply the avg in them, put them as they are.
        for (int i = 0; i < start; i++) {
            //System.out.println("A");
            newSound.setSampleValueAt(i, sampleArray[i].getValue());
        }

        // the middle indexes
        for (int i = start; i < sampleArray.length - (start + 1); i++) {
            //System.out.println("B");
            avg = 0;

            for (int j = -start; j < start; j++) {
                avg += sampleArray[i + j].getValue();
            }
            avg /= windowsSize;
            sampleArray[i].setValue(avg);

        }

        // the end index where we can't apply the avg in them, put them as they are.
        for (int i = sampleArray.length - (start + 1); i < sampleArray.length; i++) {
            //System.out.println("C");
            newSound.setSampleValueAt(i, sampleArray[i].getValue());
        }

    }

    public void weightedAverage(short type) {

        double[] weighted = null;
        if (type == 0) { // left distrbution
            weighted = new double[]{0.15, 0.3, 0.25, 0.2, 0.1};
        } else if (type == 1) { // middle normal skewed
            weighted = new double[]{0.1, 0.2, 0.4, 0.2, 0.1};
        } else if (type == 2) { // right skewed
            weighted = new double[]{0.1, 0.2, 0.25, 0.3, 0.15};
        }

        SoundSample[] sampleArray = this.getSamples();

        int start = (int) Math.floor(5 / 2);
        int avg = 0;
        int ii = 0;

        for (int i = start; i < sampleArray.length - (start + 1); i++) {
            avg = 0;
            ii = 0;
            for (int j = -start; j < start; j++) {
                avg += (sampleArray[j].getValue() * weighted[ii]);
                ii++;
            }
            avg /= 5;
            sampleArray[i].setValue(avg);

        }

    }

Moving rectangle

In these two methods we make a rectangle and make it move from one corner to another. 

The method that we used:

    private void TopLeftToBottomRightActionPerformed(java.awt.event.ActionEvent evt) {                                                     
        //take the time form the user 
        int time = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration in seconds"));
        
        int FPS = 30;
        Picture p ;
        Graphics g ;
        
    FrameSequencer frameSequencer =  new FrameSequencer("Movie");
    
    //for each 1 SEC 30 FPS
    for (int i = 0; i < FPS*time; i++){
        
      p = new Picture(640,480);
      g = p.getGraphics();
      g.setColor(Color.BLACK);
      g.fillRect(i * 10, i * 7, 70,70);
      
      
      frameSequencer.addFrame(p);
    }
    
    frameSequencer.play(FPS);
  

    }                                                    
    
    private void TopRightToBottomLeftActionPerformed(java.awt.event.ActionEvent evt) {                                                     
        //take the time form the user 
        int time = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration in seconds"));

        int FPS = 30;
        Picture p ;
        Graphics g ;
        
    FrameSequencer frameSequencer =  new FrameSequencer("Movie");
    
    //for each 1 SEC 30 FPS
    for (int i = 0; i < FPS*time; i++){
      p = new Picture(640,480);
      g = p.getGraphics();
      g.setColor(Color.BLACK);
      g.fillRect(Math.abs((i * 10) - 570), i * 7, 70,70);
        
      
      frameSequencer.addFrame(p);
    }
    
    frameSequencer.play(FPS);
  
    }    

show trajectory

at the beginning of this method, the user must enter the duration value, according to this value a movie of frames will be made that shows an orange rectangle moving down with a circler way using some mathematical modules such as Sin/Cos.

The method that we used:

    private void SinAndCosActionPerformed(java.awt.event.ActionEvent evt) {                                          
       try{
           int duration = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration in seconds"));
         
              int framesPerSec = 30;
        Picture p = null;
        Graphics g = null;
        FrameSequencer frameSequencer
                = new FrameSequencer("Movie");
        frameSequencer.setShown(true);

        // loop through the first second
        for (int i = 0; i < framesPerSec * duration; i++) {
            // draw a filled rectangle
            p = new Picture(640, 480);
            g = p.getGraphics();
            g.setColor(Color.ORANGE);
            g.fillRect(50 + (int) (10 * Math.sin(i)),
                    4 * i + (int) (10 * Math.cos(i)),
                    50, 50);

            // add frame to sequencer
            frameSequencer.addFrame(p);
        }

        // play the movie
        frameSequencer.play(framesPerSec);
            
            
            } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(null, "Time must be an integer", "Error", JOptionPane.ERROR_MESSAGE);
        }
       
    }

Showing trajectory of dropped ball

This method shows a drawn ball using a movie of frames and the ball is dropping then bouncing WITHOUT reaching the same high until it starts rolling on the floor, this method also need the user to enter the duration of the movie.

The method that we used:

    private void DroppedballActionPerformed(java.awt.event.ActionEvent evt) {                                            
        try {
            int time = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration in seconds"));
            int framesPerSec = 30;
            Picture p = null;
            Graphics g = null;
            FrameSequencer frameSequencer = new FrameSequencer("Movie");

            frameSequencer.setShown(true);
            int distance = 150;
            int height = 480;
            int start = (height - 50) - distance;
            for (int i = 0; i < framesPerSec * time; i++) {
                p = new Picture(640, height);
                g = p.getGraphics();
                int y = (int) (Math.sin(30 + (i)) * distance) + start;
                if (y > (height - 50 - 10)) {
                    distance = distance / 2;
                    start = (height - 50) - distance;
                }
                y = (int) (Math.sin(30 + (i)) * distance) + start;
                g.setColor(Color.GRAY);
                g.fillOval(i * 20, y, 50, 50);
                // add frame to sequencer
                frameSequencer.addFrame(p);
            }
            // play the movie
            frameSequencer.play(framesPerSec);
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(null, "Time must be an integer", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }  

Ticker tape movie

This method requires the user to enter the duration and a message, the message will be shown as a movie of frames that move to right and left the screen, and it depends on the duration.

The method that we used:

    private void TickerTapeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_TickerTapeActionPerformed
        try {
            int time = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration"));
            String message = JOptionPane.showInputDialog("please enter your message");
            int framesPerSec = 30;
            Picture p = null;
            Graphics g = null;
            FrameSequencer frameSequencer = new FrameSequencer("Movie");
            Font font = new Font("Arial", Font.BOLD, 24);
            boolean right = true;
            // loop for 2 seconds of animation
            for (int j = 0, k = 0; j < framesPerSec * time; j++) {
                // draw the string
                p = new Picture(500, 400);
                g = p.getGraphics();
                g.setColor(Color.BLACK);
                g.setFont(font);
                if (right) {
                    if (k * 10 > p.getWidth() - message.length() * 12) {
                        right = false;
                    }
                } else {
                    if (k < 1) {
                        right = true;
                    }
                }
                if (right) {
                    g.drawString(message, k++ * 10, 200);
                } else {
                    g.drawString(message, k-- * 10, 200);
                }
                // add frame to sequencer
                frameSequencer.addFrame(p);
            }

            // play the movie
            frameSequencer.play(framesPerSec);
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(null, "Time must be an integer", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_TickerTapeActionPerformed

Cropping movie

This method will play a movie that shows a cropping piece of a selected picture moving around, and the user must enter the duration.

The method that we used:

    private void CroppingActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CroppingActionPerformed
         try{  
         int duration = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration in seconds"));
         // load the picture 
         String fName = "C:\\Users\\3boody\\Desktop\\midi.jpg";
         //String fName = FileChooser.pickAFile();
        Picture pic = new Picture(fName);

        // declare other variables
        Picture target = null;   // targeted piece of pic
        FrameSequencer frameSequencer
                = new FrameSequencer("Movie");
        int framesPerSec = 30;

        // loop creating the frames
        for (int i = 0; i < framesPerSec * duration; i++) {
            target = new Picture(640, 480);
            target.copy(pic, 250, 170, 390, 300, i * 10, i * 5);
            frameSequencer.addFrame(target);
        }

        // play the movie
        frameSequencer.play(framesPerSec);
            
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(null, "Time must be an integer", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_CroppingActionPerformed

Create a sunset movie

this method is about making a beach picture looks like a sunset movie we used the method that was there in the MovieMaker class[6]
The method that we used:

    public void makeSunsetMovie(String directory, double value, int time) {
        // load the picture of the beach
        String fName = FileChooser.pickAFile();
        Picture beachP = new Picture(fName);

        // declare other variables
        Picture target = null;
        FrameSequencer frameSequencer= new FrameSequencer(directory);
        int framesPerSec = 30;

        frameSequencer.setShown(true);

        // loop creating the frames
        for (int i = 0; i < framesPerSec * time; i++) {
            beachP.makeSunset(100 - i * value);
            frameSequencer.addFrame(beachP);
        }

        // play the movie
        frameSequencer.play(framesPerSec);
    }

Background subtraction from movie

First should choose 3 picture 1- foregrounds pic 2- old back ground 3- new back ground, then will make object for frame per second after that make loop to swap between old with new back ground.

The method that we used:

    private void BackgroundActionPerformed(java.awt.event.ActionEvent evt) {                                           
            try {
            int time = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration"));
            // load the pictures
            JFileChooser fc = new JFileChooser("C:\\Users\\alish\\Desktop\\SoMultimedia");
            fc.showOpenDialog(null);
            String foreground = fc.getSelectedFile().getAbsolutePath();
            Picture foregroundPic = null;

            fc.showOpenDialog(null);
            String oldBG = fc.getSelectedFile().getAbsolutePath();
            Picture oldBGPic = new Picture(oldBG);

            fc.showOpenDialog(null);
            String newBG = fc.getSelectedFile().getAbsolutePath();
            Picture newBGPic = new Picture(newBG);

            // declare other variables
            FrameSequencer frameSequencer = new FrameSequencer("Movie_Edit");
            int framesPerSec = 30;
            frameSequencer.setShown(true);
            // loop creating the frames
            for (int i = 0; i < framesPerSec * time; i++) {
                foregroundPic = new Picture(foreground);
                foregroundPic.swapBackground(oldBGPic, newBGPic, i);
                frameSequencer.addFrame(foregroundPic);
            }

            // play the movie
            frameSequencer.play(framesPerSec);
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(null, "Time must be an integer", "Error", JOptionPane.ERROR_MESSAGE);
        }
    } 

Edge detection

First you should choose the picture then initialize object for frame per second then make loop and copy picture and store it in object then we make to loops to go through pixels and store top and bottom pixels after that will get color average for it , then make if statements to use formula for this method.

The method that we used:

    private void EdgeDetectionActionPerformed(java.awt.event.ActionEvent evt) {                                              
          
        try {
            int time = Integer.parseInt(JOptionPane.showInputDialog("please enter movie duration"));
            JFileChooser fc = new JFileChooser("C:\\Users\\alish\\Desktop\\SoMultimedia");
            fc.showOpenDialog(null);
            String fName = fc.getSelectedFile().getAbsolutePath();
            Picture pic = new Picture(fName);

            Picture copyPict = null;
       
            FrameSequencer frameSequencer = new FrameSequencer("movie_Edit");
            int framesPerSec = 30;
            
            for (int i = 0; i < framesPerSec * time; i++) {
                copyPict = new Picture(pic);
                double topAverage = 0.0;
                double bottomAverage = 0.0;

                for (int x = 0; x < pic.getHeight() - 1; x++) {
                    for (int y = 0; y < copyPict.getWidth(); y++) {
                        Pixel topPixel = copyPict.getPixel(y, x);
                        Pixel bottomPixel = copyPict.getPixel(y, x + 1);

                        topAverage = (topPixel.getRed() + topPixel.getGreen() + topPixel.getBlue()) / 3.0;
                        bottomAverage = (bottomPixel.getRed() + bottomPixel.getGreen() + bottomPixel.getBlue()) / 3.0;

                        if (Math.abs(topAverage - bottomAverage) < (time * framesPerSec + 1) - i) {
                            topPixel.setColor(Color.WHITE);
                        } else {
                            topPixel.setColor(Color.BLACK);
                        }
                    }
                }
               
                frameSequencer.addFrame(copyPict);
            }
            
            frameSequencer.play(framesPerSec);
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(null, "Time must be an integer", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }
Download the full projectOr view it on GitHub

Group memebers

Abdulmajeed Alahmadi

Ali Abumansour

Abdullah Binsaid 

Mohannad Alotaibi