天天看點

java圖像處理——圖像讀取,二值化轉bitset

最近要處理一些新聞中的廣告圖檔,其中比較多的是含二維碼的圖檔。簡單寫了3種邏輯處理了下(同源不同内容,同圖檔;含完整二維碼的圖檔;殘缺二維碼廣告圖檔),基本達到了實際需求。有同樣需求的可以參考下,言歸正傳,今天看到有讀者問java圖像怎麼轉bitset,下面舉例一些我的處理方法。

首先圖檔有不同來源,有的是url,那麼url首先讀取成BufferedImage

URL url_url = new URL(img_url);
Image image = null;
try
{
    image = ImageIO.read(url_url);
} catch (Exception e)
{
    e.printStackTrace();

}
BufferedImage buf = GetImage.toBufferedImage(image);
BufferedImage binatryBuf = BinaryTest.loaclTOBinatry(buf);
ImageData imagedata = BufferedImageHelper.bufferedImageToImageData(binatryBuf);
           

其中的imagedata是個model類,寬,高,和圖像的bitset。

public class ImageData {
    private BitSet bitSet;
    private int width;
    private int height;

    public ImageData(BitSet bitSet, int width, int height) {
        this.bitSet = bitSet;
        this.width = width;
        this.height = height;
    }

    public BitSet getBitSet() {
        return bitSet;
    }

    public void setBitSet(BitSet bitSet) {
        this.bitSet = bitSet;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}
           

其中的GetImage.toBufferedImage(image)

public static BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage)image;
         }

         image = new ImageIcon(image).getImage();
         BufferedImage bimage = null;
         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            int transparency = Transparency.OPAQUE;
             GraphicsDevice gs = ge.getDefaultScreenDevice();
             GraphicsConfiguration gc = gs.getDefaultConfiguration();
             bimage = gc.createCompatibleImage(
             image.getWidth(null), image.getHeight(null), transparency);
         } catch (HeadlessException e) {
         }

        if (bimage == null) {
            int type = BufferedImage.TYPE_INT_RGB;
             bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
         }

         Graphics g = bimage.createGraphics();

         g.drawImage(image, , , null);
         g.dispose();

        return bimage;
    }
           

如果BufferedImage本身就是二值的黑白圖像,比如商場列印發票,那麼可以用下面方法直接轉換為bitset(Imagedata)

public static ImageData bufferedImageToImageData(BufferedImage buffer,
            int threshold) {
        if (buffer == null) {
            return null;
        }
        int width = buffer.getWidth();
        int height = buffer.getHeight();
        BitSet bitSet = new BitSet(width * height);
        for (int y = ; y < height; y++) {
            for (int x = ; x < width; x++) {
                bitSet.set(y * width + x, buffer.getRGB(x, y) <= threshold);
            }
        }
        return new ImageData(bitSet, width, height);
    }
public static ImageData bufferedImageToImageData(BufferedImage buffer) {
        return bufferedImageToImageData(buffer, new Color(, , ).getRGB());
    }
           

如果圖像是彩色的,那麼就要選擇合适的二值化方法,下面舉例幾種:

适合檢測類似二維碼這種黑白色彩對比明顯的圖檔,max-min方法

/**
     * 3*3視窗求Max-Min
     * 
     * @param gray
     * @param x
     * @param y
     * @param w
     * @param h
     * @return
     */
    public static int getMaxMinColor(int[][] gray, int x, int y, int w, int h, int edge)
    {
        int max = ;
        int min = ;
        for (int i = -edge; i < edge + ; i++)
        {
            for (int j = -edge; j < edge + ; j++)
            {
//              System.out.println("x:"+(x+i));
//              System.out.println("y:"+(y+j));
                if (x + i <  || x + i > w-)
                    continue;
                if (y + j <  || y + j > h-)
                    continue;
                if (gray[x + i][y + j] > max)
                    max = gray[x + i][y + j];
                if (gray[x + i][y + j] < min)
                    min = gray[x + i][y + j];
            }
        }

        return max - min >  ? max - min : ;
    }
           

或者簡單的視窗均值,這裡也是3×3

/**
     * 自己加周圍8個灰階值再除以9,算出其相對灰階值
     * 
     * @param gray
     * @param x
     * @param y
     * @param w
     * @param h
     * @return
     */
    public static int getAverageColor(int[][] gray, int x, int y, int w, int h)
    {
        int rs = gray[x][y] + (x ==  ?  : gray[x - ][y]) + (x ==  || y ==  ?  : gray[x - ][y - ])
                + (x ==  || y == h -  ?  : gray[x - ][y + ]) + (y ==  ?  : gray[x][y - ])
                + (y == h -  ?  : gray[x][y + ]) + (x == w -  ?  : gray[x + ][y])
                + (x == w -  || y ==  ?  : gray[x + ][y - ])
                + (x == w -  || y == h -  ?  : gray[x + ][y + ]);
        return rs / ;
    }
           

這樣每個像素點會因為不同的計算方法(或者說不同程度受周邊像素權重影響),得到一個表示值,和經驗門檻值進行對比,進行二值。當然還有其他二值方法,大多是微分或者類卷積的對梯度變化加強。

public static BufferedImage loaclTOBinatry(BufferedImage bi,int T) throws IOException
    {
        int h = bi.getHeight();// 擷取圖像的高
        int w = bi.getWidth();// 擷取圖像的寬
        int rgb = bi.getRGB(, );// 擷取指定坐标的ARGB的像素值
        int[][] gray = new int[w][h];
        for (int x = ; x < w; x++)
        {
            for (int y = ; y < h; y++)
            {
                gray[x][y] = getGray(bi.getRGB(x, y));
            }
        }

        BufferedImage nbi = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
        int SW = T;// 200=0.84 //230=0.94
        for (int x = ; x < w; x++)
        {
            for (int y = ; y < h; y++)
            {
                // if(gray[x][y]<SW){
                // if (getAverageColor(gray, x, y, w, h) > SW)//局部均值
                // {
                if (getMaxMinColor(gray, x, y, w, h, ) > SW)//局部拉伸
                {
                    int max = new Color(, , ).getRGB();
                    nbi.setRGB(x, y, max);
                } else
                {
                    int min = new Color(, , ).getRGB();
                    nbi.setRGB(x, y, min);
                }
            }
        }
        return nbi;
        // ImageIO.write(nbi, "jpg", new File("D:/Test/binary/二值化後_無壓縮.jpg"));
    }

    public static int getGray(int rgb)
    {
        Color c = new Color(rgb);
        int r = c.getRed();
        int g = c.getGreen();
        int b = c.getBlue();
        int top = (r + g + b) / ;
        return (int) (top);
    }
           

url轉bufferImage還可以用下面的方法

public static BufferedImage getBufferedImage(String img_url)
    {
        BufferedInputStream bis = null;
        HttpURLConnection httpUrl = null;
        URL url = null;
        try
        {
            url = new URL(img_url);
            httpUrl = (HttpURLConnection) url.openConnection();
            httpUrl.connect();
            bis = new BufferedInputStream(httpUrl.getInputStream());
            InputStream iis = bis;
            BufferedImage image = ImageIO.read(iis);

            return image;
        } catch (MalformedURLException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e)
        {
            e.printStackTrace();
        }

        return null;
    }
           

以上就這些吧,有興趣的可以評論留言,我看到會盡快回答的。