CPIT-380 Group Project
Supervised with care by Doctor Saim Ahmed Rasheed
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); } }