java - 为什么此图像比较会使我的应用程序崩溃?

标签 java

我正在尝试编写一个简单的程序,允许用户选择引用图像 A,然后获取同一目录中的所有图像,将它们标准化为相同的尺寸(300x300),提取特征,计算与A 的特征,并按照从较远到最远的顺序显示它们。

作为 Java 初学者,我在代码方面遇到了一些麻烦,我的代码没有错误,并且我能够运行该程序,但是当我运行应用程序时,应用程序崩溃,控制台日志为:

> Exception in thread "main" java.lang.NoClassDefFoundError: com.sun.media.jai.codec.SeekableStream
    at javax.media.jai.operator.BMPDescriptor.class$(BMPDescriptor.java:95)
    at javax.media.jai.operator.BMPDescriptor.<clinit>(BMPDescriptor.java:94)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at javax.media.jai.RegistryFileParser.getInstance(RegistryFileParser.java:224)
    at javax.media.jai.RegistryFileParser.registerDescriptor(RegistryFileParser.java:360)
    at javax.media.jai.RegistryFileParser.parseFile(RegistryFileParser.java:295)
    at javax.media.jai.RegistryFileParser.loadOperationRegistry(RegistryFileParser.java:55)
    at javax.media.jai.OperationRegistry.initializeRegistry(OperationRegistry.java:371)
    at javax.media.jai.JAI.<clinit>(JAI.java:566)
    at imagePr.NaiveSimilarityFinder.rescale(NaiveSimilarityFinder.java:116)
    at imagePr.NaiveSimilarityFinder.<init>(NaiveSimilarityFinder.java:46)
    at imagePr.NaiveSimilarityFinder.main(NaiveSimilarityFinder.java:223)

这是 Java 文件的代码:

    9 import java.awt.BorderLayout;
  10 import java.awt.Color;
  11 import java.awt.Container;
  12 import java.awt.Font;
  13 import java.awt.GridLayout;
  14 import java.awt.image.RenderedImage;
  15 import java.awt.image.renderable.ParameterBlock;
  16 import java.io.File;
  17 import java.io.IOException;
  18  
  19 import javax.imageio.ImageIO;
  20 import javax.media.jai.InterpolationNearest; 
  21 import javax.media.jai.JAI;
  22 import javax.media.jai.iterator.RandomIter;
  23 import javax.media.jai.iterator.RandomIterFactory;
  24 import javax.swing.JFileChooser;
  25 import javax.swing.JFrame;
  26 import javax.swing.JLabel;
  27 import javax.swing.JOptionPane;
  28 import javax.swing.JPanel;
  29 import javax.swing.JScrollPane;
  30  
  31 import com.sun.media.jai.widget.DisplayJAI;
  32 /**
  33  * This class uses a very simple, naive similarity algorithm to compare an image
  34  * with all others in the same directory.
  35  */
  36 public class NaiveSimilarityFinder extends JFrame
  37   {
  38   // The reference image "signature" (25 representative pixels, each in R,G,B).
  39   // We use instances of Color to make things simpler.
  40   private Color[][] signature;
  41   // The base size of the images.
  42   private static final int baseSize = 300;
  43   // Where are all the files?
  44   private static final String basePath = 
  45     "C:\\imagecmp";
  46   
  47  /*
  48   * The constructor, which creates the GUI and start the image processing task.
  49   */
  50   public NaiveSimilarityFinder(File reference) throws IOException
  51     {
  52     // Create the GUI
  53     super("Naive Similarity Finder");
  54     Container cp = getContentPane();
  55     cp.setLayout(new BorderLayout());
  56     // Put the reference, scaled, in the left part of the UI.
  57     RenderedImage ref = rescale(ImageIO.read(reference));
  58     cp.add(new DisplayJAI(ref), BorderLayout.WEST);
  59     // Calculate the signature vector for the reference.
  60     signature = calcSignature(ref);
  61     // Now we need a component to store X images in a stack, where X is the
  62     // number of images in the same directory as the original one.
  63     File[] others = getOtherImageFiles(reference);
  64     JPanel otherPanel = new JPanel(new GridLayout(others.length, 2));
  65     cp.add(new JScrollPane(otherPanel), BorderLayout.CENTER);
  66     // For each image, calculate its signature and its distance from the
  67     // reference signature.
  68     RenderedImage[] rothers = new RenderedImage[others.length];
  69     double[] distances = new double[others.length];
  70     for (int o = 0; o < others.length; o++)
  71       {
  72       rothers[o] = rescale(ImageIO.read(others[o]));
  73       distances[o] = calcDistance(rothers[o]);
  74       }
  75     // Sort those vectors *together*.
  76     for (int p1 = 0; p1 < others.length - 1; p1++)
  77       for (int p2 = p1 + 1; p2 < others.length; p2++)
  78         {
  79         if (distances[p1] > distances[p2])
  80           {
  81           double tempDist = distances[p1];
  82           distances[p1] = distances[p2];
  83           distances[p2] = tempDist;
  84           RenderedImage tempR = rothers[p1];
  85           rothers[p1] = rothers[p2];
  86           rothers[p2] = tempR;
  87           File tempF = others[p1];
  88           others[p1] = others[p2];
  89           others[p2] = tempF;
  90           }
  91         }
  92     // Add them to the UI.
  93     for (int o = 0; o < others.length; o++)
  94       {
  95       otherPanel.add(new DisplayJAI(rothers[o]));
  96       JLabel ldist = new JLabel("<html>" + others[o].getName() + "<br>"
  97           + String.format("% 13.3f", distances[o]) + "</html>");
  98       ldist.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 36));
  99       System.out.printf("<td class=\"simpletable legend\"> "+
 100           "<img src=\"MiscResources/ImageSimilarity/icons/miniicon_%s\" "+
 101           "alt=\"Similarity result\"><br>% 13.3f</td>\n", others[o].getName(),distances[o]);
 102       otherPanel.add(ldist);
 103       }
 104     // More GUI details.
 105     pack();
 106     setVisible(true);
 107     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 108     }
 109  
 110  /*
 111   * This method rescales an image to 300,300 pixels using the JAI scale
 112   * operator.
 113   */
 114   private RenderedImage rescale(RenderedImage i)
 115     {
 116     float scaleW = ((float) baseSize) / i.getWidth();
 117     float scaleH = ((float) baseSize) / i.getHeight();
 118     // Scales the original image
 119     ParameterBlock pb = new ParameterBlock();
 120     pb.addSource(i);
 121     pb.add(scaleW);
 122     pb.add(scaleH);
 123     pb.add(0.0F);
 124     pb.add(0.0F);
 125     pb.add(new InterpolationNearest());
 126     // Creates a new, scaled image and uses it on the DisplayJAI component
 127     return JAI.create("scale", pb);
 128     }
 129   
 130  /*
 131   * This method calculates and returns signature vectors for the input image.
 132   */
 133   private Color[][] calcSignature(RenderedImage i)
 134     {
 135     // Get memory for the signature.
 136     Color[][] sig = new Color[5][5];
 137     // For each of the 25 signature values average the pixels around it.
 138     // Note that the coordinate of the central pixel is in proportions.
 139     float[] prop = new float[]
 140       {1f / 10f, 3f / 10f, 5f / 10f, 7f / 10f, 9f / 10f};
 141     for (int x = 0; x < 5; x++)
 142       for (int y = 0; y < 5; y++)
 143         sig[x][y] = averageAround(i, prop[x], prop[y]);
 144     return sig;
 145     }
 146  
 147  /*
 148   * This method averages the pixel values around a central point and return the
 149   * average as an instance of Color. The point coordinates are proportional to
 150   * the image.
 151   */
 152   private Color averageAround(RenderedImage i, double px, double py)
 153     {
 154     // Get an iterator for the image.
 155     RandomIter iterator = RandomIterFactory.create(i, null);
 156     // Get memory for a pixel and for the accumulator.
 157     double[] pixel = new double[3];
 158     double[] accum = new double[3];
 159     // The size of the sampling area.
 160     int sampleSize = 15;
 161     int numPixels = 0;
 162     // Sample the pixels.
 163     for (double x = px * baseSize - sampleSize; x < px * baseSize + sampleSize; x++)
 164       {
 165       for (double y = py * baseSize - sampleSize; y < py * baseSize + sampleSize; y++)
 166         {
 167         iterator.getPixel((int) x, (int) y, pixel);
 168         accum[0] += pixel[0];
 169         accum[1] += pixel[1];
 170         accum[2] += pixel[2];
 171         numPixels++;
 172         }
 173       }
 174     // Average the accumulated values.
 175     accum[0] /= numPixels;
 176     accum[1] /= numPixels;
 177     accum[2] /= numPixels;
 178     return new Color((int) accum[0], (int) accum[1], (int) accum[2]);
 179     }
 180  
 181  /*
 182   * This method calculates the distance between the signatures of an image and
 183   * the reference one. The signatures for the image passed as the parameter are
 184   * calculated inside the method.
 185   */
 186   private double calcDistance(RenderedImage other)
 187     {
 188     // Calculate the signature for that image.
 189     Color[][] sigOther = calcSignature(other);
 190     // There are several ways to calculate distances between two vectors,
 191     // we will calculate the sum of the distances between the RGB values of
 192     // pixels in the same positions.
 193     double dist = 0;
 194     for (int x = 0; x < 5; x++)
 195       for (int y = 0; y < 5; y++)
 196         {
 197         int r1 = signature[x][y].getRed();
 198         int g1 = signature[x][y].getGreen();
 199         int b1 = signature[x][y].getBlue();
 200         int r2 = sigOther[x][y].getRed();
 201         int g2 = sigOther[x][y].getGreen();
 202         int b2 = sigOther[x][y].getBlue();
 203         double tempDist = Math.sqrt((r1 - r2) * (r1 - r2) + (g1 - g2)
 204             * (g1 - g2) + (b1 - b2) * (b1 - b2));
 205         dist += tempDist;
 206         }
 207     return dist;
 208     }
 209  
 210  /*
 211   * This method get all image files in the same directory as the reference.
 212   * Just for kicks include also the reference image.
 213   */
 214   private File[] getOtherImageFiles(File reference)
 215     {
 216     File dir = new File(reference.getParent());
 217     // List all the image files in that directory.
 218     File[] others = dir.listFiles(new JPEGImageFileFilter());
 219     return others;
 220     }
 221  
 222  /*
 223   * The entry point for the application, which opens a file with an image that
 224   * will be used as reference and starts the application.
 225   */
 226   public static void main(String[] args) throws IOException
 227     {
 228     JFileChooser fc = new JFileChooser(basePath);
 229     fc.setFileFilter(new JPEGImageFileFilter());
 230     int res = fc.showOpenDialog(null);
 231     // We have an image!
 232     if (res == JFileChooser.APPROVE_OPTION)
 233       {
 234       File file = fc.getSelectedFile();
 235       new NaiveSimilarityFinder(file);
 236       }
 237     // Oops!
 238     else
 239       {
 240       JOptionPane.showMessageDialog(null,
 241           "You must select one image to be the reference.", "Aborting...",
 242           JOptionPane.WARNING_MESSAGE);
 243       }
 244     }
 245   
 246   }

快速猜猜我可能做错了什么?我正在 Windows 机器上的 Eclipse 中进行编码。

最佳答案

我不确定是否有人仍然对这个问题感兴趣,但我对此很好奇,并试图找到相关代码的来源。它似乎起源于这里:Java Image Processing Cookbook

为了测试代码,我只是设置了一个简单的基于 Maven 的 Java 项目,当然遇到了问题,Java 11 中不再有 Java 媒体框架! ;-)

所以我将这个很好的 Springsource 依赖项添加到我的项目 pom.xml 文件中:

<dependencies>
    <dependency>
        <groupId>javax.media.jai</groupId>
        <artifactId>com.springsource.javax.media.jai.core</artifactId>
        <version>1.1.3</version>
    </dependency>
</dependencies>

为了访问 springsource 存储库,我还添加了此存储库信息:

<repositories>
    <repository>
        <id>com.springsource.repository.bundles.external</id>
        <name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
        <url>http://repository.springsource.com/maven/bundles/external</url>
    </repository>
</repositories>

完成此操作后,该项目已成功编译并按预期运行。它计算所选图像(运行时打开文件选择器)与同一目录中的所有其他图像之间的非常简单的相似性索引。

然而,有一个小缺点,因为寻找缺失的 mediaLib 加速器来加速处理似乎存在问题。它看起来像这样:

Error: Could not find mediaLib accelerator wrapper classes. Continuing in pure Java mode.
Occurs in: com.sun.media.jai.mlib.MediaLibAccessor
java.lang.NoClassDefFoundError: com/sun/medialib/mlib/Image
    at com.sun.media.jai.mlib.MediaLibAccessor$1.run(MediaLibAccessor.java:248)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:310)
[...]

幸运的是,计算运行仍然非常快,并且结果非常有趣;-) 如果用于图像的非常基本的相似性比较的基本 Java 成像代码,那是多么漂亮的小片段啊!做得好! :)

更新:要消除有关缺少加速器的错误,请通过将以下内容添加到代码中来更改此环境变量:

System.setProperty("com.sun.media.jai.disableMediaLib", "true");

请参阅此处以获取我使用的图像的示例输出。要了解这些值,请参阅 Java Image Processing Cookbook还有:

<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_action-ancient-architecture-231013.jpg" alt="Similarity result"><br>        0,000</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_army-blur-figurines-231014.jpg" alt="Similarity result"><br>     2169,529</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_action-armed-army-231012.jpg" alt="Similarity result"><br>     2525,682</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_army-attack-figurines-1214270.jpg" alt="Similarity result"><br>     2610,023</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_bowl-cereal-bowl-cereals-135525.jpg" alt="Similarity result"><br>     3388,865</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_blue-bowl-bright-1375811.jpg" alt="Similarity result"><br>     3392,382</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_aqua-blue-clean-1201625.jpg" alt="Similarity result"><br>     4121,866</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_background-berries-blue-674689.jpg" alt="Similarity result"><br>     4133,818</td>
<td class="simpletable legend"> <img src="MiscResources/ImageSimilarity/icons/miniicon_blue-bright-citrus-405031.jpg" alt="Similarity result"><br>     4746,385</td>
<小时/>

关于java - 为什么此图像比较会使我的应用程序崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31319512/

相关文章:

java - 实例化接口(interface)对象 - 如何?

java - 无法在 Ebean 服务器中注册类(Play Framework 2 - Java)

java stream List<Map<String,Double>> 需要根据字符串取平均值

java - 以记事本方式打开 CSV 文件,然后看不到新行

java - 使用 MOXy 使用默认的 minOccurs 生成模式

java - JAVA/Android中从字符串中提取多个子字符串

Java运行速度变慢

javascript - 如何使用 Ajax 和 Servlet 上传文件

java - 如何彻底清洁我的面板

java - 从 JPA 实体查找用于 DDL 生成的 jpa-ddl-maven-plugin 时出错