灰度图是单通道图,像素只有一个值:灰度值。灰度值越高,则图像越亮。
现在我们已经知道我们看到一张灰度图是由许多不同灰度值的像素点构成,而每个像素就是一个越亮的像素,灰度值越高,最高值是255(白色),越暗的像素,灰度值越低,最低值是0(黑色)。灰度值在0~255之间的像素呈现不同程度的灰色。这样,通过不同的灰度层次,图像也就展现出来了。
我们已经知道我们看到一张灰度图是由许多不同灰度值的像素点构成,而每个像素就是一个代表灰度值的数字,那么我们想想许多数字按照图像的形状成矩形排列,会变成什么呢?没错,学过线性代数的小伙伴一下子就猜出来了,就是——矩阵。
首先在pom中引入opencv的依赖
<dependency><groupId>org.openpnp</groupId><artifactId>opencv</artifactId><version>4.5.1-1</version> </dependency>
<dependency><groupId>net.sourceforge.tess4j</groupId><artifactId>tess4j</artifactId><version>5.3.0</version> </dependency><!-- 以下以opencv为例,添加你需要的库的相关依赖 --> <dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.7</version> </dependency>
灰度值的范围为0-255,根据灰度值的范围和红外图的右侧温度调范围21.5-124 建立线性关系
public static float map(int value, int min1, int max1, float min2, float max2) {return (float) (min2 + (value - min1) * (max2 - min2) / (max1 - min1)); } public static double getTempture(int pixe) {double mappedValue=0.0;for (int i = 0; i <= 248; i++) {if (i==pixe){mappedValue = map(i, 0, 248, 21.5f, 124f);}}return mappedValue; }
计算面积的完整代码如下:
import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc;import java.util.ArrayList; import java.util.Collections; import java.util.List;import static org.opencv.highgui.HighGui.imshow; import static org.opencv.imgcodecs.Imgcodecs.imread;public class InfraredTemperature {static {System.loadLibrary(Core.NATIVE_LIBRARY_NAME);}public static float map(int value, int min1, int max1, float min2, float max2) {return (float) (min2 + (value - min1) * (max2 - min2) / (max1 - min1));}public static double getTempture(int pixe) {double mappedValue=0.0;for (int i = 0; i <= 248; i++) {if (i==pixe){mappedValue = map(i, 0, 248, 21.5f, 124f);}}return mappedValue;}public static void main(String[] args) {// 读取图像 IMREAD_GRAYSCALEMat img = Imgcodecs.imread("C:\\Users\\lenovo\\Desktop\\999.jpg", Imgcodecs.IMREAD_GRAYSCALE );// 阈值double thresholdValue = 110;// 创建一个全黑的同样大小的图片,用于存储结果Mat result = Mat.zeros(img.size(), CvType.CV_8UC1);// 计算超过阈值的面积double totalArea = img.size().height * img.size().width;double thresholdedArea = 0;double surplusArea =0;List<Integer> listOfDoubles = new ArrayList<>();// 遍历图像中的每个像素for (int y = 0; y < img.size().height; y++) {for (int x = 0; x < img.size().width; x++) {int pixelValue =(int) Math.round(img.get(y, x)[0]) ;listOfDoubles.add(pixelValue);}}//灰度值的最大值System.out.println("count is " + Collections.max(listOfDoubles));for (int y = 0; y < img.size().height; y++) {for (int x = 0; x < img.size().width; x++) {//该值为实际温度和最大像素值相除得来的double pixelValue = img.get(y, x)[0];double pixelValueInfo =getTempture((int) Math.round(pixelValue));if (pixelValueInfo > thresholdValue) {result.put(y, x, 248); // 将超过阈值的点设置为白色thresholdedArea++;}else {surplusArea++;}}}// 计算超过阈值的面积所占的百分比double areaFraction = thresholdedArea / totalArea;// 输出结果int countInfo=0;for (int i = 0; i < listOfDoubles.size(); i++) {if (listOfDoubles.get(i)==248){countInfo++;}}//最大灰度值出现的次数System.out.println("countInfo is " + countInfo);//超过阈值的面积System.out.println("thresholdedArea is " + thresholdedArea);//低于阈值的面积System.out.println("surplusArea is " + surplusArea);System.out.println("totalArea is " + totalArea);System.out.println("Area of pixels with temperature over " + thresholdValue + "°C: " + areaFraction);// 如果需要可视化结果,可以显示result图片Imgcodecs.imwrite("C:\\Users\\lenovo\\Desktop\\888.jpg", result);// 释放资源img.release();result.release();}}
其中还有获取红外图右侧温度条的逻辑
即用java调用ocr库实现红外图上文本的获取,代码如下
<dependency><groupId>net.sourceforge.tess4j</groupId><artifactId>tess4j</artifactId><version>5.3.0</version> </dependency>
import com.spire.ocr.OcrScanner; import org.apache.commons.io.FileUtils;import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern;public class InfraredTemperatureExtractor {public static void main(String[] args) throws Exception {BufferedImage image = ImageIO.read(new File("D:\\13.jpg"));// 获取图片的宽度和高度int width = image.getWidth();int height = image.getHeight();// 创建一个新的图片,保留原来的类型和颜色空间 // * x: 裁剪起点横坐标 // * y: 裁剪起点纵坐标 // * w: 需要裁剪的宽度 // * h: 需要裁剪的高度BufferedImage croppedImage = image.getSubimage(width / 2, 0, width / 2, height);try {// 写入新图片ImageIO.write(croppedImage, "jpg", new File("D:\\13_1.jpg"));//指定依赖文件的路径String dependencies = System.getProperty("user.dir") + "\\dependencies";//指定要需要扫描的图片的路径String imageFile = "D:\\13_1.jpg"; // RandomAccessFile raf = new RandomAccessFile(imageFile, "rw");//创建OcrScanner对象,并设置其依赖文件路径OcrScanner scanner = new OcrScanner();scanner.setDependencies(dependencies);//扫描指定的图像文件scanner.scan(imageFile);//获取扫描的文本内容String scannedText = scanner.getText().toString();List<Double> numbers = new ArrayList<>();// 分割字符串// 将每行转换为整数并添加到列表中String[] lines = scannedText.split("\n");for (int i = 0; i < lines.length; i++) {if (i != lines.length - 1) {numbers.add(Double.parseDouble(lines[i]));}}System.out.println(numbers);} catch (Exception e) {e.printStackTrace();}File imageFileInfo = new File("D:\\13_1.jpg");try {boolean fileDeletable = imageFileInfo.canWrite();System.out.println("文件是否可以删除" +fileDeletable);//回收System.gc();imageFileInfo.delete(); // FileUtils.forceDelete(imageFileInfo);} catch (Exception e) {e.printStackTrace();}}// public static void delete(File imageFileInfo) { // boolean s=false; // try { // System.gc(); // FileUtils.forceDelete(imageFileInfo); // } catch (IOException e) { // throw new RuntimeException(e); // } // } }
//指定依赖文件的路径 String dependencies = System.getProperty("user.dir") + "\\dependencies";
安装包可以自行下载,和项目放在指定目录下即可
还有一个坑 OcrScanner scanner = new OcrScanner();没有关闭流的操作,在删除剪裁好的文件时可能出现无法删除的现象,这个时候可以用System.gc();回收程序对图片的占用,从而达到删除剪裁好的图片的效果。