Java的Webp支持

13

随着我们的基于网络的应用程序爱上了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://dev59.com/CmMm5IYBdhLWcg3wHMJ0 - José Luis
5个回答

2
原始答案的bitbucket链接不再可用,但可以找到原始存储库的分叉,例如:https://github.com/sejda-pdf/webp-imageio 我尝试使用上述GitHub中提到的webp-imageio实现,但在生产环境中使用了2天后,我得到了一个来自崩溃整个服务器的本地库的分段错误。
我转而使用谷歌提供的编译工具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" );
  }
}

进程终止后读取输出和错误流时会出现问题。如果流缓冲区已满,您的进程将被阻塞。最好在等待或使用管道时立即流式传输两个流的内容。 - Martin Kersten
嗯,听起来不太好。你能否发布一些代码以展示如何按照你所提到的方式处理流?或者详细说明一下如何重现这个问题?我尝试在一个会导致错误的文件上调用转换为webp的方法,执行了1万次,并没有看到任何问题。 - Aldo Canepa
你可以创建一个批处理文件,编写大量输出并将其作为进程从Java应用程序中执行。问题在于,如果您有一个打印大量输出的进程,则会出现问题。如果您的进程不这样做,那么就没问题了。这也取决于操作系统/Java缓冲区对于输出的设置。我在大约5年前遇到过这个问题。它可能已经修复,但我怀疑它(因为没有解决方案,您只能在发生问题时推动边界)。 - Martin Kersten
1
一个解决方案是不使用process.waitFor,而是在process.isAlive上自旋。当它仍在运行时,您可以同时使用流并询问字节是否可用,然后将其从流中移除并打印出来(或执行其他操作)。这样这些流就永远不会溢出。 - Martin Kersten
这真的很奇怪。我在我的项目https://thevegcat.com中使用`webp-imageio`处理照片已经有几年了,从来没有遇到过任何错误。 - undefined
在我的情况下,我处理用户上传的图片,所以变量相当高,也许如果一个文件有无效的内容,就会发生这种情况?:shrug - undefined

2
请使用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和数据库中,很少存储在文件系统中。

你是如何将本地库链接到你的Java项目中的? - Dušan Salay

1
使用 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")
        }
    }

1

对于来自搜索引擎的Java开发人员,我已将@Dušan Salay的答案转换为Java:

private byte[] encodeToWebP(byte[] data) {
    Mat matImage = Imgcodecs.imdecode(new MatOfByte(data), Imgcodecs.IMREAD_UNCHANGED);
    MatOfInt parameters = new MatOfInt(Imgcodecs.IMWRITE_WEBP_QUALITY, 100);
    MatOfByte output = new MatOfByte();
    Imgcodecs.imencode(".webp", matImage, output, parameters);
    return output.toArray();
}

我已经使用了Apache Commons的readFileToByteArray函数。另外,您需要首先在静态块中加载库。

static {
    OpenCV.loadLocally();
}

0

10
似乎存储库已被删除。 - Enmanuel Rodríguez Paz
2
有人找到了任何维护的替代品吗? - lalo
1
这里是分叉的存储库,也许会有所帮助:https://github.com/sejda-pdf/webp-imageio - horvoje

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接