Webp 支持 java

Webp support for java

随着我们基于网络的应用程序爱上 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;
}

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

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

未完全 java 实施,但与其他人相比非常容易

原始答案的 bitbucket link 不再可用,但可以找到原始存储库的分支,例如:https://github.com/sejda-pdf/webp-imageio

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

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

就我而言,我需要将其他图像格式转换为 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" );
  }
}

请使用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);
}

我将它转换为 byte [] 数组,因为我主要将它存储在 S3 和 DB 中,而很少存储在文件系统中。

在 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")
        }
    }