首页 > 编程语言 >BMP 图像文件解析及直方图均衡化算法(Java)

BMP 图像文件解析及直方图均衡化算法(Java)

时间:2022-11-28 17:12:48浏览次数:70  
标签:Java int 均衡化 height BMPImage width 直方图 new public

BMP图像解析,基本照抄关于Java读取和编写BMP文件的总结

直方图均衡化没抄着,自己写了一个。

代码结构:

GUI:

Java Swing实现

import Utils.BMPImage;
import Utils.GraphUtil;

import javax.swing.*;
import java.awt.*;

public class GUI extends JFrame {
    public String path="./src/main/resources/Panda4.bmp";

    public void init(){
        this.setTitle("BMP解析");//设置标题
        this.setSize(600,350);//设置窗体大小
        this.setDefaultCloseOperation(3);//点击关闭,程序自动退出。
        this.setResizable(false);//设置窗体大小不可以调节
        this.setLocationRelativeTo(null);//设置窗体出现在屏幕中间
        this.setLayout(new BorderLayout());

        JPanel panel1=new JPanel();
        panel1.setBorder(BorderFactory.createEmptyBorder(10,10,0,10));
        panel1.setLayout(new GridLayout(1,2,0,5));
        BMPImage bmpImage = new BMPImage(this.path);
        panel1.add(bmpImage);

        JPanel panel2=new JPanel();
        JButton button1=new JButton("直方图均衡化");
        button1.setFocusable(false);
        panel2.add(button1);
        button1.addActionListener(e -> {
            BMPImage bmpImage2=GraphUtil.HistogramEqualization(bmpImage);
            panel1.add(bmpImage2);
            SwingUtilities.updateComponentTreeUI(panel1);
        });

        this.add(panel1,BorderLayout.CENTER);
        this.add(panel2,BorderLayout.SOUTH);

        this.setVisible(true);
    }
    public static void main(String[] args) {
        GUI gui = new GUI();
        gui.init();
    }
}
View Code

BMPImage:

重载paint函数来显示图片。注意这段代码只能打开24-bit的bmp。

package Utils;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;

public class BMPImage extends JPanel {
    public int width;

    public int height;

    public int[][] red, green, blue;

    public BMPImage(String fileName) {
        try {
            java.io.FileInputStream fin = new java.io.FileInputStream(fileName);

            java.io.BufferedInputStream bis = new java.io.BufferedInputStream(
                    fin);

            // 建立两个字节数组来得到文件头和信息头的数据
            byte[] array1 = new byte[14];
            bis.read(array1, 0, 14);

            byte[] array2 = new byte[40];
            bis.read(array2, 0, 40);

            // 翻译bmp文件的数据,即将字节数据转化为int数据
            // 通过翻译得到位图数据的宽和高
            width = ChangeInt(array2, 7);
            height = ChangeInt(array2, 11);

            // 调用可以将整个位图数据读取成byte数组的方法
            getInf(bis);

            fin.close();
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public BMPImage(int width,int height){
        this.width=width;
        this.height=height;
        red = new int[height][width];
        green = new int[height][width];
        blue = new int[height][width];
    }

    public int ChangeInt(byte[] array2, int start) {
        // 因为char,byte,short这些数据类型经过运算符后会自动转为成int数据类,
        // 所以array2[start]&0xff的实际意思就是通过&0xff将字符数据转化为正int数据,然后在进行位运算。
        // 这里需要注意的是<<的优先级别比&高,所以必须加上括号。

        int i = (int) ((array2[start] & 0xff) << 24)
                | ((array2[start - 1] & 0xff) << 16)
                | ((array2[start - 2] & 0xff) << 8)
                | (array2[start - 3] & 0xff);
        return i;
    }

    public void getInf(java.io.BufferedInputStream bis) {
        // 给数组开辟空间
        red = new int[height][width];
        green = new int[height][width];
        blue = new int[height][width];

        // 通过计算得到每行计算机需要填充的字符数。
        // 为什么要填充?这是因为windows系统在扫描数据的时候,每行都是按照4个字节的倍数来读取的。
        // 因为图片是由每个像素点组成。而每个像素点都是由3个颜色分量来构成的,而每个分量占据1个字节。
        // 因此在内存存储中实际图片数据每行的长度是width*3。
        int skip_width = 0;
        int m = width * 3 % 4;
        if (m != 0) {
            skip_width = 4 - m;
        }

        // 通过遍历给数组填值
        // 这里需要注意,因为根据bmp的保存格式。
        // 位图数据中height的值如果是正数的话:
        // 那么数据就是按从下到上,从左到右的顺序来保存。这个称之为倒向位图。
        // 反之就是按从上到下,从左到右的顺序来保存。这个则称之为正向位图。
        for (int i = height - 1; i >= 0; i--) {
            for (int j = 0; j < width; j++) {
                try {
                    // 这里遍历的时候,一定要注意本来像素是有RGB来表示,
                    // 但是在存储的时候由于windows是小段存储,所以在内存中是BGR顺序。
                    blue[i][j] = bis.read();
                    green[i][j] = bis.read();
                    red[i][j] = bis.read();

                    // 这里一定要知道,其实系统在给位图数据中添加填充0的时候,都是加在每行的最后。
                    // 但是我们在使用dis.skipBytes()这个方法的时候,却不一定要在最后一列。
                    // 系统在填充数据的时候,在数据上加了标记。
                    // 所以dis.skipBytes()这个方法只要调用了,那么系统就会自动不读取填充数据。
                    if (j == 0) {
                        bis.skip(skip_width);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                try {
                    g.setColor(new Color(red[i][j], green[i][j], blue[i][j]));
                    g.fillRect(j, i, 1, 1);// 这里可以使用画点的任何方法,除了上面那种特例。
                }catch (Exception e){
                    continue;
                }
            }
        }
    }
}
View Code

GraphUtil放处理图片的方法。

HistogramEqualization: 直方图均衡化

灰度计算公式:

red * 0.3 + green * 0.59 + blue * 0.11

直方图均衡化过程:计算每一灰度值所拥有像素在全部像素中比例,依次进行累加,将其前缀和作为该灰度在新图像中的映射值。原理大概是累计分布函数大致是均匀的。

package Utils;

import java.util.HashMap;
import java.util.Map;

public class GraphUtil {
    public static BMPImage HistogramEqualization(BMPImage img){
        int[][] gray=new int[img.height][img.width];
        Map<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<img.height;i++){
            for(int j=0;j<img.width;j++){
                try {
                    gray[i][j] = (int) (img.red[i][j] * 0.3 + img.green[i][j] * 0.59 + img.blue[i][j] * 0.11);
                    if (map.containsKey(gray[i][j])) {
                        map.put(gray[i][j], map.get(gray[i][j]) + 1);
                    } else {
                        map.put(gray[i][j], 1);
                    }
                }catch (Exception e){
                    System.out.println(i+" "+j);
                }
            }
        }
        Map<Integer,Integer> map2=new HashMap<>();
        int ans=0;
        int cnt=img.width*img.height;
        for(Map.Entry<Integer,Integer> entry:map.entrySet()) {
            ans+=entry.getValue();
            int newval=(int)(((double)ans/cnt)*256);
            map2.put(entry.getKey(),newval);
        }
        for(int i=0;i<img.height;i++){
            for(int j=0;j<img.width;j++){
                gray[i][j]=map2.get(gray[i][j]);
            }
        }
        BMPImage img2=new BMPImage(img.width,img.height);
        for(int i=0;i<img.height;i++){
            for(int j=0;j<img.width;j++){
                img2.red[i][j]=gray[i][j];
                img2.green[i][j]=gray[i][j];
                img2.blue[i][j]=gray[i][j];
            }
        }
        return img2;
    }
}
View Code

放两张运行结果图:

 

 熊猫的眼睛和猫猫条纹都出来了,不错。

标签:Java,int,均衡化,height,BMPImage,width,直方图,new,public
From: https://www.cnblogs.com/capterlliar/p/16932680.html

相关文章

  • Java多线程经典概念题
    Java多线程经典概念题1.并行和并发有什么区别?并发(concurrency)和并行(parallellism)是:解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间......
  • Java多线程经典编程题
    Java多线程经典编程题1.要求线程a执行完才开始线程b,线程b执行完才开始线程packagecom.example.javatest.theardTest.MultiThreadAlgorithm;/**要求线程a执行完才......
  • Java实现递归查询树结构
        我们在实际开发中,肯定会用到树结构,如部门树、菜单树等等。Java后台利用递归思路进行构建树形结构数据,返回给前端,能以下拉菜单等形式进行展示。今天,咱们就来说......
  • JAVA-API概述-Scanner类键盘录入数据
    代码一packagecom.itheima.api;importjava.util.Scanner;publicclassDemo1Scanner{/*next():遇到了空格,就不再录入数据了结......
  • 小新学Java13-【线程池、Lambda表达式】
    一、等待唤醒机制1.1线程间通信概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。1.2等待唤醒机制什么是等待唤醒机制?这是多个线程间的一种协作机......
  • Java基础运算符
    JAVA基础运算符算数运算符:+,-,*,/,%,++,--//二元运算符//Ctrl+D赋值当前行到下一行inta=10;intb=20;intc=25;......
  • Java中数组、集合初始化及遍历方式
    一、数组1.一维数组一维数组两种初始化方式静态初始化int[]array={1,2,3};int[]array=newint[]{1,2,3};动态初始化int[]array=newint[3......
  • Java语言程序设计第六讲,流与文件
    这次知识点总结拖了好久QWQ因为没有找到相关文件(.java文件之类的资料),这次的总结会比之前的简略很多 流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数......
  • Java工程师常见面试题分享
    Java工程师常见面试题目汇总!我们想要成为Java工程师首先会经过各种面试,面试就离不开面试题目,今天来和大家分享一下10个Java工程师面试题目!1、静态加载和动态加载有什么区别......
  • JAVA面试题
    1Synchronized锁升级的原理答:Synchronized在jdk1.6之前是通过重量级锁的方式实现。重量级锁底层是通过MutexLock来实现互斥锁的一个功能,Mutex是系统方法,调用的时候用户......