【问题标题】:Webp support for java对 Java 的 Webp 支持
【发布时间】:2017-05-02 08:00:24
【问题描述】:

随着我们基于网络的应用程序爱上 webp 图像格式,我发现自己需要一个可以解码它的方法或库,

我已经写了这段代码,但它只是错过了本机解码器(我更喜欢它是一个 jar 库):

public BufferedImage decodeWebP(byte[] encoded, int w, int h) {
    int[] width = new int[]{w};
    int[] height = new int[]{h};

    byte[] decoded = decodeRGBAnative(encoded);  //here is the missing part , 
    if (decoded.length == 0) return null;

    int[] pixels = new int[decoded.length / 4];
    ByteBuffer.wrap(decoded).asIntBuffer().get(pixels);

    BufferedImage bufferedImage = new BufferedImage(width[0], height[0], BufferedImage.TYPE_INT_RGB);

    //  bufferedImage.setRGB(x, y, your_value);

    int BLOCK_SIZE = 3;

    for(int r=0; r< height[0]; r++) {
        for (int c = 0; c < width[0]; c++) {
            int index = r * width[0] * BLOCK_SIZE + c * BLOCK_SIZE;
            int red = pixels[index] & 0xFF;
            int green = pixels[index + 1] & 0xFF;
            int blue = pixels[index + 2] & 0xFF;
            int rgb = (red << 16) | (green << 8) | blue;
            bufferedImage.setRGB(c, r, rgb);
        }
    }
    return bufferedImage;
}

【问题讨论】:

标签: java webp


【解决方案1】:

请使用 OpenCV。我使用 maven 和 org.openpnp:opencv:4.5.1-2。对存储在 Mat 中的图像进行编码所需的只是:

public static byte [] encodeWebp(Mat image, int quality) {
    MatOfInt parameters = new MatOfInt(Imgcodecs.IMWRITE_WEBP_QUALITY, quality);
    MatOfByte output = new MatOfByte();
    if(Imgcodecs.imencode(".webp", image, output, parameters)) 
        return output.toArray();
    else
        throw new IllegalStateException("Failed to encode the image as webp with quality " + quality);
}

我将其转换为字节 [] 数组,因为我主要将其存储在 S3 和 DB 中,而不是在文件系统中。

【讨论】:

  • 你是如何将原生库链接到你的 java 项目的?
【解决方案2】:

原始答案的bitbucket链接不再可用,但可以从原始存储库中找到fork,例如:https://github.com/sejda-pdf/webp-imageio

我尝试使用上述 github 中的 webp-imageio 实现,但在生产中使用了 2 天后,我遇到了来自本机库的分段错误,导致整个服务器崩溃。

我在这里求助于使用 google 提供的编译工具:https://developers.google.com/speed/webp/download 并做一个小的包装类来调用二进制文件。

就我而言,我需要从其他图像格式转换为 webp,因此我需要“cwebp”二进制文件。我写的包装是:

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.concurrent.TimeUnit;

public class ImageWebpLibraryWrapper {

  private static final String CWEBP_BIN_PATH = "somepath/libwebp-1.1.0-linux-x86-64/bin/cwebp";

  public static boolean isWebPAvailable() {
    if ( CWEBP_BIN_PATH == null ) {
      return false;
    }
    return new File( CWEBP_BIN_PATH ).exists();
  }

  public static boolean convertToWebP( File imageFile, File targetFile, int quality ) {
    Process process;
    try {
      process = new ProcessBuilder( CWEBP_BIN_PATH, "-q", "" + quality, imageFile.getAbsolutePath(), "-o",
          targetFile.getAbsolutePath() ).start();
      process.waitFor( 10, TimeUnit.SECONDS );
      if ( process.exitValue() == 0 ) {
        // Success
        printProcessOutput( process.getInputStream(), System.out );
        return true;
      } else {
        printProcessOutput( process.getErrorStream(), System.err );
        return false;
      }
    } catch ( Exception e ) {
      e.printStackTrace();
      return false;
    }
  }
  
  private static void printProcessOutput( InputStream inputStream, PrintStream output ) throws IOException {
    try ( InputStreamReader isr = new InputStreamReader( inputStream );
        BufferedReader bufferedReader = new BufferedReader( isr ) ) {
      String line;
      while ( ( line = bufferedReader.readLine() ) != null ) {
        output.println( line );
      }
    }
  }

围绕 ImageIO 的实现更好,但我不能让生产服务器崩溃的分段错误。

示例用法:

public static void main( String args[] ) {
  if ( !isWebPAvailable() ) {
    System.err.println( "cwebp binary not found!" );
    return;
  }
  File file = new File( "/home/xxx/Downloads/image_11.jpg" );
  File outputFile = new File( "/home/xxx/Downloads/image_11-test.webp" );
  int quality = 80;
  if ( convertToWebP( file, outputFile, quality ) ) {
    System.out.println( "SUCCESS" );
  } else {
    System.err.println( "FAIL" );
  }
}

【讨论】:

  • 进程终止后读取输出+错误流时出现问题。如果流缓冲区已满,您的进程将阻塞。最好在等待或使用管道时立即流式传输两个流的内容。
  • 嗯,这听起来很糟糕。您能否发布一些有关如何按照您提到的方式处理流的代码?或者详细说明如何重现问题?我尝试在导致错误的文件上调用转换为 webp 的方法,执行了 10.000 次,没有发现任何问题。
  • 您可以创建一个写入大量输出的批处理文件,然后在 Java 应用程序中将其作为进程执行。如果您有一个打印大量输出的进程,就会出现问题。如果你的过程不这样做,你会没事的。它还取决于输出的 OS / Java 缓冲区。大约 5 年前,我遇到过这个问题。它可能已经修复,但我对此表示怀疑(因为没有修复,您只能在问题发生时推动边界)。
  • 解决方案是不使用 process.waitFor,而是使用 process.isAlive。当它仍在运行时,您可以使用两个流并询问字节是否可用,然后从流中删除它们并将它们打印出来(或其他)。这样这些流就不会溢出。
【解决方案3】:

在所有可能的搜索中,这个是最好和最简单的:

https://bitbucket.org/luciad/webp-imageio

不是完整的java实现,但很容易与其他人比较

【讨论】:

  • 仓库好像被删除了
【解决方案4】:

在 kotlin 中使用 OpenPnP OpenCV:

fun encodeToWebP(image: ByteArray): ByteArray {
        val matImage = Imgcodecs.imdecode(MatOfByte(*image), Imgcodecs.IMREAD_UNCHANGED)
        val parameters = MatOfInt(Imgcodecs.IMWRITE_WEBP_QUALITY, 100)
        val output = MatOfByte()
        if (Imgcodecs.imencode(".webp", matImage, output, parameters)) {
            return output.toArray()
        } else {
            throw IllegalStateException("Failed to encode the image as webp")
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-18
    • 2011-07-31
    • 2022-01-03
    • 2016-08-02
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多