1, Environment configuration
This program needs to import capture-0.3.10 jar,bridj-0.6.2.jar,slf4j-api-1.7.2.jar and slf4j-nop-1.7.26 Jar four jar packages.
2, Realization thought
A total of three classes are required, namely, the interface display class (drawpixel), the image (video) processing class (myThread) and the button listener class (button). Among them, myThread class is a subclass of Thread class, which makes the video processing process parallel to the main program. It mainly calls the camera to take a photo every certain time, and adopts gray, white, black and white Mosaic, water mark, corrosion and expansion, simple Gaussian blur, magnifying glass and harping glass process the photos, and display the processing results in the specified area. When the interval is short enough, the video recorded by the camera can be processed. The DrawPixel class implements the creation of a window. The first line of the window is the nine button corresponding to the nine processing methods (including the original camera). The button is below the video display area, and the nine buttons register listeners, get the brush and transmit the brush to MyThread class object MT and Button class object b, transfer object MT to b, and finally call mt.start(). The button class monitors nine buttons. When a user clicks a button, the content of the clicked button will be passed to the object MT, so as to switch the video processing mode.
3, Concrete implementation
3.1 MyThread class
3.1.1 properties
private String name="Original camera"; private Graphics g;
The setG method obtains the window brush, and the setN method is called by object b, which is the basis for switching video processing methods.
3.1.2 gray
Grayscale the picture and display it. The R, G and B of each pixel of the picture are the average values of the R, G and B of the pixels at the same position of the original picture.
public void grayscale(BufferedImage img) { int grayscalevalue,value; Color color; for(int i=0;i<img.getWidth();i++) { for(int j=0;j<img.getHeight();j++) { color=new Color(img.getRGB(i, j)); value=(color.getRed()+color.getGreen()+color.getBlue())/3; grayscalevalue=value<<8; grayscalevalue=grayscalevalue+value; grayscalevalue=grayscalevalue<<8; grayscalevalue=grayscalevalue+value; img.setRGB(i,j,grayscalevalue); } } g.drawImage(img, 0, 75, null); }
3.1.3 black and white
The picture is processed in black and white and displayed. Judge the average value of R, G and B at each position of the original picture. If it is less than 128, set the RGB value of the pixel to 0, otherwise set it to 16777215.
public void blackAndWhite(BufferedImage img ) { Color color; int value; for(int i=0;i<img.getWidth();i++) { for(int j=0;j<img.getHeight();j++) { color=new Color(img.getRGB(i, j)); value=(color.getRed()+color.getGreen()+color.getBlue())/3; if(value<128)img.setRGB(i,j,0); else img.setRGB(i,j,16777215); } } g.drawImage(img, 0, 75, null); }
3.1.4 mosaic
Mosaic and display the picture, divide the original picture into several 10 * 10 areas, and set the pixel value in each area as the pixel value in the upper left corner of the area.
public void mosaic(BufferedImage img ) { for(int i=0;i<img.getWidth();i++) { for(int j=0;j<img.getHeight();j++) { img.setRGB(i,j,img.getRGB(i/10*10, j/10*10)); } } g.drawImage(img, 0, 75, null); }
3.1.5 dynamic water texture
Add dynamic water texture to the video and display it. Firstly, cut out the water texture area centered on the water texture and store it in the array m_iHeightField1, and then iterate 50 times to obtain the water texture effect. Finally, the picture with water texture is drawn to the buffer pTargetImage and displayed. In video processing, the water texture radius of the picture is changed to realize the dynamic water texture in the video.
private int radius=50;//The initial radius of water grain shall not exceed 240 public void ripple(BufferedImage img ) { radius+=15; if(radius>240)radius=50; int m_iWidth=img.getWidth(), m_iHeight=img.getHeight(); int xx=m_iWidth/2,yy=m_iHeight/2;//Horizontal and vertical coordinates of water grain center int[] m_iHeightField1 = new int[m_iWidth* m_iHeight];//Store pixel values int[] m_iHeightField2 = new int[m_iWidth* m_iHeight];//Store pixel values int m_iHpage = 0;//Represents the array to be updated during the iteration int m_density = 5;//Water grain density int height=300;//Array M_ Initial value of pixels in water texture area in iheightfield1 int rquad=radius * radius;//radius^2 int cyq; //The water grain area is cut out with the water grain as the center and exists in the array M_ In iheightfield1 for (int cy = -radius; cy < radius; cy++) { cyq = cy * cy; for (int cx = -radius; cx <radius; cx++) { if (cx * cx + cyq < rquad) { m_iHeightField1[m_iWidth * (cy + yy) + (cx + xx)] += height; } } } //Iterate 50 times to get the water texture effect int newh; int[] newptr; int[] oldptr; for (int i = 0; i < 50; i++) { int count = m_iWidth + 1; // Set up the pointers if (m_iHpage == 0) { newptr = m_iHeightField1; oldptr = m_iHeightField2; } else { newptr = m_iHeightField2; oldptr = m_iHeightField1; } for (int y = (m_iHeight - 1) * m_iWidth; count < y; count += 2) { for (int x = count + m_iWidth - 2; count < x; count++) { //Eight pixel method newh = ((oldptr[count + m_iWidth] + oldptr[count - m_iWidth] + oldptr[count + 1] + oldptr[count - 1] + oldptr[count - m_iWidth - 1] + oldptr[count - m_iWidth + 1] + oldptr[count + m_iWidth - 1] + oldptr[count + m_iWidth + 1]) >> 2) - newptr[count]; newptr[count] = newh - (newh >> m_density); } } m_iHpage ^= 1;//Bitwise XOR } //Draw the picture with water texture to the buffer pTargetImage BufferedImage pTargetImage= new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB); int dx, dy; int c; int offset = m_iWidth + 1; int lIndex; int lBreak = m_iWidth * m_iHeight; int[] ptr = m_iHeightField1; for (int y = (m_iHeight - 1) * m_iWidth; offset < y; offset += 2) { for (int x = offset + m_iWidth - 2; offset < x; offset++) { if (offset + m_iWidth >= m_iWidth * m_iHeight)continue; dx = ptr[offset] - ptr[offset + 1]; dy = ptr[offset] - ptr[offset + m_iWidth]; lIndex = offset + m_iWidth * (dy >> 3) + (dx >> 3); if (lIndex < lBreak && lIndex > 0) { if( lIndex / m_iWidth<m_iHeight)c =img.getRGB( lIndex % m_iWidth, lIndex / m_iWidth); else c =img.getRGB( lIndex % m_iWidth, m_iHeight-1); int R; int G; int B; int ir; int ig; int ib; R =(c >> 16) & 0x00ff - dx; G = (c >> 8) & 0x00ff - dx; B = c & 0x00ff - dx; ir = (R < 0) ? 0 : (R > 255) ? 255 : R; ig = (G < 0) ? 0 : (G > 255) ? 255 : G; ib = (B < 0) ? 0 : (B > 255) ? 255 : B; c = (0x00ff & ir) << 16 | (0x00ff & ig) << 8 | (0x00ff & ib); if( offset / m_iWidth<m_iHeight)pTargetImage.setRGB( offset % m_iWidth, offset / m_iWidth,c); else pTargetImage.setRGB( offset % m_iWidth, m_iHeight-1,c); } offset++; if (offset + m_iWidth >= m_iWidth * m_iHeight)continue; dx = ptr[offset] - ptr[offset + 1]; dy = ptr[offset] - ptr[offset + m_iWidth]; lIndex = offset + m_iWidth * (dy >> 3) + (dx >> 3); if (lIndex < lBreak && lIndex > 0) { if( lIndex / m_iWidth<m_iHeight)c =img.getRGB( lIndex % m_iWidth, lIndex / m_iWidth); else c =img.getRGB( lIndex % m_iWidth, m_iHeight-1); int R; int G; int B; int ir; int ig; int ib; R =(c >> 16) & 0x00ff - dx; G = (c >> 8) & 0x00ff - dx; B = c & 0x00ff - dx; ir = (R < 0) ? 0 : (R > 255) ? 255 : R; ig = (G < 0) ? 0 : (G > 255) ? 255 : G; ib = (B < 0) ? 0 : (B > 255) ? 255 : B; c = (0x00ff & ir) << 16 | (0x00ff & ig) << 8 | (0x00ff & ib); if( offset / m_iWidth<m_iHeight)pTargetImage.setRGB( offset % m_iWidth, offset / m_iWidth,c); else pTargetImage.setRGB( offset % m_iWidth, m_iHeight-1,c); } } } //Repair possible damage Color color; for(int i=0;i<pTargetImage.getWidth();i++) { for(int j=0;j<pTargetImage.getHeight();j++) { color=new Color(pTargetImage.getRGB(i, j)); if(color.getRed()==0&&color.getGreen()==0&&color.getBlue()==0) { pTargetImage.setRGB(i, j, img.getRGB(i, j)); } } } g.drawImage(pTargetImage, 0, 75, null); }
3.1.6 corrosion and expansion
Corrosion: shift the structural element B by a to obtain ba. If Ba is included in X, we write down this point a. the set composed of all points a satisfying the above conditions is called the result of erosion of X by B. Expressed by the formula: E(X)={a| Ba X}=X B.
Expansion: it can be regarded as the dual operation of corrosion. Its definition is: after translating the structural element B by a, we get ba. If Ba hits x, we write down this point a. The set composed of all points a satisfying the above conditions is called the result of the expansion of X by B. Expressed by the formula: D(X)={a | Ba ↑ X}=X | B.
/* * Image opening operation: first corrosion and then expansion * @param sourceImage Grayscale images or binary images are processed here * @param shreshold :Threshold - when the expansion result is less than the threshold, the value of the image position is still set to 0; During corrosion operation, * When the gray value is greater than or equal to the threshold (less than the threshold) and the structural element is 1 (0), it is considered that the corresponding position is matched; * If it is a binary image, 1 should be passed in. */ public void corrodeAndDilate(BufferedImage img ) { int sData[]={0,0,0,0,1,0,0,1,1};//Structural elements int threshold=50;//threshold int width=img.getWidth(); int height=img.getHeight(); //Convert picture to grayscale int[][] source=new int[height][width]; for(int j=0;j<height;j++) { for(int i=0;i<width;i++) { source[j][i]=(img.getRGB(i, j)>>16)&0xFF; } } int[][] result1=new int[height][width];//Used to store corrosion results ///First corrosion operation for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { //Do not operate at the edge, only operate within the edge if(i>0&&j>0&&i<height-1&&j<width-1) { int max =0; ///Traversal of structural elements for(int k=0;k<sData.length;k++) { int x=k/3;///The quotient represents the x offset int y=k%3;///The remainder represents the y offset if(sData[k]!=0) { //When it is not 0, it must all be greater than the threshold, otherwise it will be set to 0 and end the traversal if(source[i-1+x][j-1+y]>=threshold) { if(source[i-1+x][j-1+y]>max) { max=source[i-1+x][j-1+y]; } } else { //It does not match the structure element. Assign a value of 0 and end the traversal max=0; break; } } } result1[i][j]=max; } else { result1[i][j]=source[i][j];//Edge direct assignment }//end of the most out if-else clause . } }//end of outer for clause //Post expansion operation int[][] result=new int[height][width];//Used to store expansion results for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { //The edge does not operate if(i>0&&j>0&&i<height-1&&j<width-1) { int max =0; //Traversal of structural elements for(int k=0;k<sData.length;k++) { int x=k/3;///The quotient represents the x offset int y=k%3;///The remainder represents the y offset if(sData[k]!=0) { //When the structural element is not 0, the maximum value of the corresponding item in the image is taken out and assigned to the current position of the image as the gray value if(result1[i-1+x][j-1+y]>max) { max=result1[i-1+x][j-1+y]; } } } //The threshold can be set here. When max is less than the threshold, it is set to 0 if(max<threshold) { result[i][j]=0; } else { result[i][j]=max; } } else { result[i][j]=result1[i][j];//Edge direct assignment } } } //The expansion result is converted to gray image BufferedImage targetImage=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for(int j=0;j<height;j++) { for(int i=0;i<width;i++) { int greyRGB=result[j][i]; int rgb=(greyRGB<<16)|(greyRGB<<8)|greyRGB; targetImage.setRGB(i, j, rgb); } } g.drawImage(targetImage, 0, 75, null); }
3.1.7 simple Gaussian blur
Simple Gaussian blur processing and display of the picture. Set the R, G and b values of each pixel in the original picture as the mean value of R, G and B of the surrounding 8 pixels respectively.
public void vague(BufferedImage img ) { int height = img.getHeight(); int width = img.getWidth(); int[][] martrix = new int[3][3];//fuzzy matrix for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { //Find the fuzzy matrix of each pixel int current = 0; for (int m= i-1; m < i+2; m++) { for (int n=j-1; n < 2+j; n++) { int tx = m; if (tx < 0) { tx = -tx; } else if (tx >= img.getWidth()) { tx = i; } int ty = n; if (ty < 0) { ty = -ty; } else if (ty >= img.getHeight()) { ty = j; } martrix[current/3][current%3] = img.getRGB(tx, ty); current++; } } //Find the value of each pixel of the blurred picture int r = 0; int g = 0; int b = 0; for (int m = 0; m < martrix.length; m++) { for (int n = 0; n < martrix.length; n++) { if (n == 1) { continue; } Color c = new Color(martrix[m][n]); r += c.getRed(); g += c.getGreen(); b += c.getBlue(); } } img.setRGB(i, j, new Color(r / 8, g / 8, b / 8).getRGB()); } } g.drawImage(img, 0, 75, null); }
3.1.8 magnifying glass
Enlarge the specified area of the video. The center of the amplification area in this program is the video center, the radius of the amplification area is half of the video height, and the magnification is 2. If the magnification center is taken as the origin, the pixel value with coordinates (x,y) in the specified area is set to the pixel value with coordinates (x/2,y/2) of the original picture.
public void magnifier(BufferedImage img ) { int m= 2;//Magnification int w=img.getWidth(); int h=img.getHeight(); BufferedImage targetImage=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); int cenX = w/2; int cenY = h/2; int therash = h;//Enlarged area radius for (int y = 0; y < h ; y++) { for (int x = 0; x < w; x++) { if(Math.sqrt((cenX-x)*(cenX-x) +(cenY-y)*(cenY-y)) < therash) { int tx = (x - cenX)/m + cenX; int ty = (y - cenY)/m + cenY; targetImage.setRGB(x, y, img.getRGB(tx, ty)); } else { targetImage.setRGB(x, y, img.getRGB(x, y)); } } } g.drawImage(targetImage, 0, 75, null); }
3.1.9 hatching mirror
For the specified video area to achieve the effect of the hatching mirror, the center of the hatching mirror area in this program is the video center, and the radius of the hatching mirror area is half of the video height (h/2). If the center of the hatching mirror is taken as the origin, the pixel value with the coordinates of (x,y) in the specified area is set as the pixel value with the coordinates of the original picture as (2*x*d/h,*2d*y/h), where d is the distance from the point (x,y) to the origin.
public void distortingMirror(BufferedImage img ) { int w=img.getWidth(); int h=img.getHeight(); BufferedImage targetImage=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); int cenX = w/2; int cenY = h/2; int therash = h;//Enlarged area radius for (int y = 0; y < h ; y++) { for (int x = 0; x < w; x++) { int d=(int) Math.sqrt((cenX-x)*(cenX-x) +(cenY-y)*(cenY-y)); if(d < therash) { int tx = (x - cenX)*d/therash + cenX; int ty = (y - cenY)*d/therash + cenY; targetImage.setRGB(x, y, img.getRGB(tx, ty)); } else { targetImage.setRGB(x, y, img.getRGB(x, y)); } } } g.drawImage(targetImage, 0, 75, null); }
3.1.10 run function
Call the camera to take a photo every certain time, process the photo according to name, and display the processing results in the specified area. When the interval is short enough, the video recorded by the camera can be processed.
public void run() { Webcam webcam = Webcam.getDefault(); webcam.setViewSize(WebcamResolution.VGA.getSize()); try { WebcamPanel panel = new WebcamPanel(webcam); } catch(Exception e) {} while(true) { BufferedImage img = webcam.getImage(); if(name.equals("Grayscale")) { grayscale(img); } else if(name.equals("black and white")) { blackAndWhite(img); } else if(name.equals("Mosaic")) { mosaic(img); } else if(name.equals("Water grain")) { ripple(img); } else if(name.equals("Corrosion expansion")) { corrodeAndDilate(img); } else if(name.equals("vague")) { vague(img); } else if(name.equals("enlarge")) { magnifier(img); } else if(name.equals("Ha")) { distortingMirror(img); } else { g.drawImage(img, 0, 75, null); } try { Thread.sleep(1); } catch(InterruptedException e) {} } }
3.2Button class
The Button class monitors nine buttons. When a user clicks a Button, the content of the clicked Button will be passed to the object mt, so as to switch the video processing mode.
public class Button implements ActionListener { private MyThread mt; public void setMT(MyThread m) { mt=m; } public void actionPerformed(ActionEvent e) { mt.setN(e.getActionCommand()); } }
3.3DrawPixel class
The DrawPixel class implements the creation of a window. The first line of the window is the nine button corresponding to the nine processing methods (including the original camera). The button is below the video display area, and the nine buttons register listeners, get the brush and transmit the brush to MyThread class object MT and Button class object b, transfer object MT to b, and finally call mt.start().
public class DrawPixel { public static void main(String[] args) { JFrame jf=new JFrame(); jf.setLayout(new FlowLayout()); jf.setSize(640,555); jf.setLocationRelativeTo(null); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setVisible(true); jf.setResizable(false); JButton[] jb=new JButton[9]; jb[0]=new JButton("Original camera"); jb[1]=new JButton("Grayscale"); jb[2]=new JButton("black and white"); jb[3]=new JButton("Mosaic"); jb[4]=new JButton("Water grain"); jb[5]=new JButton("Corrosion expansion"); jb[6]=new JButton("vague"); jb[7]=new JButton("enlarge"); jb[8]=new JButton("Ha"); Button b=new Button(); for(int i=0;i<9;i++) { jb[i].addActionListener(b); jf.add(jb[i]); } Graphics g=jf.getGraphics(); MyThread mt=new MyThread(); mt.setG(g); b.setMT(mt); mt.start(); } }