如何在Java Swing中接收并显示.fits格式图像并执行Blob Detection?
嘿,我来一步步帮你搞定在Java Swing环境里读取、显示FITS图像,还完成Blob检测的事儿!FITS作为天文领域的标准格式,处理起来确实需要点专门的工具,咱们一步步来:
第一步:准备必要的依赖
Java本身没内置FITS文件的解析能力,所以咱们得用nom-tam-fits这个靠谱的库——它是Java生态里处理FITS的主流选择,支持各种FITS结构。另外,如果要做Blob检测,用OpenCV的Java绑定会省很多事儿,毕竟手动写Blob检测算法挺麻烦的。
如果用Maven的话,直接在pom.xml里加这两个依赖:
<!-- 处理FITS文件 --> <dependency> <groupId>nom-tam-fits</groupId> <artifactId>nom-tam-fits</artifactId> <version>1.18.0</version> </dependency> <!-- OpenCV Java绑定 --> <dependency> <groupId>org.openpnp</groupId> <artifactId>opencv</artifactId> <version>4.5.5-0</version> </dependency>
要是不用Maven,直接去官网下载对应的Jar包,加到项目的类路径里就行。
第二步:读取FITS文件并转换为Swing可用的图像
FITS文件的核心是HDU(Header Data Unit),通常主HDU里就存着图像数据。咱们要做的是读取这个HDU的像素数据,然后转换成Swing能识别的BufferedImage。
注意:FITS的像素数据可能是float、short或者其他数值类型,需要先归一化到0-255的灰度范围,这样才能显示成正常的图像。
代码示例:
import nom.tam.fits.Fits; import nom.tam.fits.ImageHDU; import java.awt.image.BufferedImage; import java.io.File; public class FitsImageReader { public static BufferedImage readFitsImage(String filePath) throws Exception { // 打开FITS文件 Fits fits = new Fits(new File(filePath)); // 获取主图像HDU ImageHDU hdu = (ImageHDU) fits.getHDU(0); // 获取像素数据(这里假设是float类型,不同FITS可能有差异,需根据实际类型调整) float[][] pixels = (float[][]) hdu.getData().getArray(); int width = pixels[0].length; int height = pixels.length; BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); // 归一化像素值到0-255 float minVal = Float.MAX_VALUE; float maxVal = Float.MIN_VALUE; for (float[] row : pixels) { for (float val : row) { if (val < minVal) minVal = val; if (val > maxVal) maxVal = val; } } float range = maxVal - minVal; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int gray = (int) (((pixels[y][x] - minVal) / range) * 255); bufferedImage.setRGB(x, y, (gray << 16) | (gray << 8) | gray); } } fits.close(); return bufferedImage; } }
如果你的FITS文件像素是short类型,就把float[][]改成short[][],然后调整归一化的逻辑就行。
第三步:在Swing中显示FITS图像
有了BufferedImage,显示就简单了。咱们写一个简单的Swing窗口,用自定义JPanel来展示图像(这种方式更灵活,方便后续画Blob检测结果):
import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; public class FitsImageViewer extends JPanel { private BufferedImage image; public FitsImageViewer(BufferedImage image) { this.image = image; // 设置窗口大小适配图像 setPreferredSize(new Dimension(image.getWidth(), image.getHeight())); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (image != null) { g.drawImage(image, 0, 0, this); } } // 用于更新图像(比如检测Blob后更新) public void updateImage(BufferedImage newImage) { this.image = newImage; repaint(); } public static void main(String[] args) { try { BufferedImage fitsImage = FitsImageReader.readFitsImage("your-image.fits"); JFrame frame = new JFrame("FITS Image Viewer"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); FitsImageViewer viewer = new FitsImageViewer(fitsImage); frame.add(new JScrollPane(viewer)); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "读取FITS文件失败:" + e.getMessage()); } } }
运行这个main方法,就能看到你的FITS图像显示在窗口里了!
第四步:实现Blob Detection
接下来是Blob检测,咱们用OpenCV来做,效率高且功能完善。步骤大概是:把BufferedImage转成OpenCV的Mat,配置BlobDetector参数,检测Blob,然后把检测结果画到图像上,再转回BufferedImage更新Swing显示。
先写Blob检测的工具类:
import org.opencv.core.*; import org.opencv.features2d.SimpleBlobDetector; import org.opencv.features2d.SimpleBlobDetector_Params; import org.opencv.imgproc.Imgproc; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.util.List; public class BlobDetector { static { // 加载OpenCV库 System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } public static BufferedImage detectBlobs(BufferedImage inputImage) { // 把BufferedImage转成OpenCV的Mat(灰度图) Mat grayMat = bufferedImageToMat(inputImage); // 配置BlobDetector参数 SimpleBlobDetector_Params params = new SimpleBlobDetector_Params(); // 设置阈值范围(从暗到亮) params.minThreshold = 10; params.maxThreshold = 200; // 过滤面积太小的Blob params.filterByArea = true; params.minArea = 20; // 过滤圆形度不够的Blob(0到1,1是完美圆形) params.filterByCircularity = true; params.minCircularity = 0.3f; // 可以根据需求调整其他参数(比如凸度、惯性矩等) SimpleBlobDetector detector = SimpleBlobDetector.create(params); MatOfKeyPoint keypoints = new MatOfKeyPoint(); detector.detect(grayMat, keypoints); // 在图像上画出Blob(用红色圆圈标记) Mat resultMat = new Mat(); Imgproc.cvtColor(grayMat, resultMat, Imgproc.COLOR_GRAY2BGR); Scalar red = new Scalar(0, 0, 255); List<KeyPoint> keypointList = keypoints.toList(); for (KeyPoint kp : keypointList) { Imgproc.circle(resultMat, new Point(kp.pt.x, kp.pt.y), (int) kp.size / 2, red, 2); } // 把Mat转回BufferedImage return matToBufferedImage(resultMat); } // BufferedImage转Mat(灰度图) private static Mat bufferedImageToMat(BufferedImage image) { Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC1); byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); mat.put(0, 0, data); return mat; } // Mat转BufferedImage private static BufferedImage matToBufferedImage(Mat mat) { int type = BufferedImage.TYPE_3BYTE_BGR; if (mat.channels() == 1) { type = BufferedImage.TYPE_BYTE_GRAY; } int bufferSize = mat.channels() * mat.cols() * mat.rows(); byte[] buffer = new byte[bufferSize]; mat.get(0, 0, buffer); BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type); image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), buffer); return image; } }
然后在FitsImageViewer的main方法里,加上Blob检测的逻辑:
// 读取图像后 BufferedImage fitsImage = FitsImageReader.readFitsImage("your-image.fits"); // 执行Blob检测 BufferedImage imageWithBlobs = BlobDetector.detectBlobs(fitsImage); // 显示检测后的图像 FitsImageViewer viewer = new FitsImageViewer(imageWithBlobs);
这样就能看到图像上用红色圆圈标记出检测到的Blob了!
如果你不想用OpenCV,也可以手动实现简单的Blob检测:先对图像做阈值化(把像素分成前景和背景),然后用洪水填充算法找连通区域,每个连通区域就是一个Blob。不过这种方法适合简单场景,复杂图像还是OpenCV更靠谱。
整个流程就是这样——先读FITS转成BufferedImage,在Swing显示,然后用OpenCV做Blob检测再更新显示。如果遇到FITS像素类型的问题,记得调整读取时的数组类型;Blob检测的参数也可以根据你的图像特性微调,比如阈值、最小面积这些。
内容的提问来源于stack exchange,提问作者Manish Biswas

