Video processor

Posted by whizzykid on Sat, 15 Jan 2022 03:41:29 +0100

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();
	}
}


 

Topics: Java Eclipse Algorithm image processing