首页 > 解决方案 > 有没有办法在 mips CPU 上加速 GraphicsMagick 的调整图像大小性能?

问题描述

我正在使用 netgear WNDR4300 运行 GraphicsMaigick 来调整 JPEG 图像的大小。这是cpuinfo,

cat /proc/cpuinfo
system type             : Atheros AR9344 rev 2
machine                 : NETGEAR WNDR4300
processor               : 0
cpu model               : MIPS 74Kc V4.12
BogoMIPS                : 278.93
wait instruction        : yes
microsecond timers      : yes
tlb_entries             : 32
extra interrupt vector  : yes
hardware watchpoint     : yes, count: 4, address/irw mask: [0x0ffc, 0x0ffc, 0x0ffb, 0x0ffb]
isa                     : mips1 mips2 mips32r1 mips32r2
ASEs implemented        : mips16 dsp dsp2
shadow register sets    : 1
kscratch registers      : 0
package                 : 0
core                    : 0
VCED exceptions         : not available
VCEI exceptions         : not available

它非常慢,每张照片都需要超过 4 分钟。

root@OpenWrt:/mnt/sda1/media/# ll
drwxrwxrwx    2 root     root        131072 Jun 10 14:07 ./
drwxrwxrwx    7 root     root        131072 Jun  8 10:18 ../
-rwxrwxrwx    1 root     root       4130026 Feb 10  2018 IMG_20180210_115807.jpg*

root@OpenWrt:/mnt/sda1/media/kidds# time gm mogrify -output-directory > /mnt/sda1/web -resize 64x64 -quality 75 IMG_20170628_100052.jpg
real    4m 19.72s
user    3m 21.86s
sys     0m 9.29s

-调整输出大小

更新:

root@OpenWrt:~# gm identify -version
GraphicsMagick 1.3.31 2018-11-17 Q8 http://www.GraphicsMagick.org/
Copyright (C) 2002-2018 GraphicsMagick Group.
Additional copyrights and licenses apply to this software.
See http://www.GraphicsMagick.org/www/Copyright.html for details.

Feature Support:
  Native Thread Safe       yes
  Large Files (> 32 bit)   yes
  Large Memory (> 32 bit)  no
  BZIP                     no
  DPS                      no
  FlashPix                 no
  FreeType                 yes
  Ghostscript (Library)    no
  JBIG                     no
  JPEG-2000                no
  JPEG                     yes
  Little CMS               no
  Loadable Modules         yes
  OpenMP                   no
  PNG                      yes
  TIFF                     yes
  TRIO                     no
  UMEM                     no
  WebP                     no
  WMF                      no
  X11                      no
  XML                      no
  ZLIB                     yes

Host type: mips-openwrt-linux-gnu

Configured using the command:
  ./configure  '--target=mips-openwrt-linux' '--host=mips-openwrt-linux' '--build=x86_64-pc-linux-gnu' '--program-prefix=' '--program-suffix=' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--libexecdir=/usr/lib' '--sysconfdir=/etc' '--datadir=/usr/share' '--localstatedir=/var' '--mandir=/usr/man' '--infodir=/usr/info' '--disable-nls' '--enable-shared' '--disable-static' '--enable-dependency-tracking' '--with-modules' '--with-threads' '--without-magick-plus-plus' '--without-perl' '--without-bzlib' '--without-dps' '--without-fpx' '--without-jbig' '--without-webp' '--with-jpeg' '--without-jp2' '--without-lcms2' '--without-lzma' '--with-png' '--with-tiff' '--without-trio' '--with-ttf' '--without-umem' '--without-wmf' '--without-xml' '--with-zlib' '--without-zstd' '--without-x' 'build_alias=x86_64-pc-linux-gnu' 'host_alias=mips-openwrt-linux' 'target_alias=mips-openwrt-linux' 'CC=mips-openwrt-linux-musl-gcc' 'CFLAGS=-Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-save

Final Build Parameters:
  CC       = mips-openwrt-linux-musl-gcc
  CFLAGS   = -Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -mips16 -minterlink-mips16 -iremap/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/build_dir/target-mips_24kc_musl/GraphicsMagick-1.3.31:GraphicsMagick-1.3.31 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -flto -Wall
  CPPFLAGS = -I/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/target-mips_24kc_musl/usr/include -I/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/target-mips_24kc_musl/include -I/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/usr/include -I/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/include/fortify -I/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/include -I/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/target-mips_24kc_musl/usr/include/freetype2
  CXX      = mips-openwrt-linux-musl-g++
  CXXFLAGS = -Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -mips16 -minterlink-mips16 -iremap/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/build_dir/target-mips_24kc_musl/GraphicsMagick-1.3.31:GraphicsMagick-1.3.31 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -flto
  LDFLAGS  = -L/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/target-mips_24kc_musl/usr/lib -L/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/target-mips_24kc_musl/lib -L/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/usr/lib -L/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/lib -znow -zrelro -L/opt/buildbot/slaves/lede-slave-tah/mips_24kc/build/sdk/staging_dir/target-mips_24kc_musl/usr/lib
  LIBS     = -lfreetype -lz -lltdl -lm -lpthread

UPDATE2:样本模式

root@OpenWrt:/mnt/sda1/media/kidds# time gm mogrify -output-directory /mnt/sda1/web -sample 64x64 -quality 75 IMG_20170628_100052.jpg
real    0m 20.76s
user    0m 8.70s
sys     0m 3.29s

-样本输出

有什么方法可以提高性能吗?我只是想调整一些照片的大小,如果有MIPS提供的一些库,我可以写一个工具用C语言调用它。

标签: mipsgraphicsmagick

解决方案


我从IJG 网站example.c上的 v9c 源代码中获取了该文件,并迅速将其破解如下,只从您的 4640x3480 输入文件中写入每 8 行和每 8 列。它创建了以下 580x435 图像,GraphicsMagick 可以在合理的时间内设法将其大小调整为 64x64,因为文件小了 64 倍。

在此处输入图像描述

你会使用:

gm convert result.ppm -resize 64x64 thumb.jpg

它只使用 1.1MB 的 RAM,因为它一次只读取 1 行 - 我测试使用:

/usr/bin/time -l ./example photo.jpg

您可以尝试抽取因子,我使用了 8,但您可以尝试 4 或 10,然后查看时间和质量之间的权衡。只需更改8文件倒数第二行的 。

代码看起来像这样 - 我"HACK"在评论中添加了这样你就可以看到我忙于在哪里制作丑陋的脏代码:

#include <stdio.h>
#include <stdlib.h>
#include "jpeglib.h"
#include <setjmp.h>

struct my_error_mgr {
  struct jpeg_error_mgr pub;    /* "public" fields */

  jmp_buf setjmp_buffer;    /* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}

GLOBAL(int)
read_JPEG_file (char * filename,int factor)
{
  /* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* We use our private extension JPEG error handler.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct my_error_mgr jerr;
  /* More stuff */
  FILE * infile;        /* source file */
  FILE * outfile;       /* result file */        // HACK
  JSAMPARRAY buffer;        /* Output row buffer */
  int row_stride;       /* physical row width in output buffer */

  /* In this example we want to open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to read binary files.
   */

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
  }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.txt for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* We may need to do some setup of our own at this point before reading
   * the data.  After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */ 
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = (*cinfo.mem->alloc_sarray)
        ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  /* Step 6: while (scan lines remain to be read) */
  /*           jpeg_read_scanlines(...); */

  // Write a simple PPM file called "result.ppm" that can be thumbnailed with GraphicsMagick
  // gm convert result.ppm -resize 64x64 thumbnail.jpg
  if ((outfile = fopen("result.ppm", "wb")) == NULL) {           // HACK
    fprintf(stderr, "can't open result.ppm\n");                  // HACK
    return 0;                                                    // HACK
  }
  int outwidth  = cinfo.output_width/factor;                     // HACK
  int outheight = cinfo.output_height/factor;                    // HACK
  fprintf(outfile,"P6\n%d %d\n255\n",outwidth,outheight);        // HACK
  unsigned char * outbuf;                                        // HACK
  outbuf = (unsigned char*)malloc(outwidth*3);                   // HACK

  int line=0;           // HACK
  while (cinfo.output_scanline < cinfo.output_height) {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    // Do every Nth row                                          // HACK
    if(line%factor==0){                                          // HACK
       int pixel,op;
       op=0;
       for(pixel=0;pixel<row_stride/3;pixel+=factor){
          outbuf[op++] = buffer[0][(pixel*3) +0];
          outbuf[op++] = buffer[0][(pixel*3) +1];
          outbuf[op++] = buffer[0][(pixel*3) +2];
       }
       fwrite(outbuf,1,op,outfile);                              // HACK
    }                                                            // HACK
    line++;                                                      // HACK
  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  fclose(infile);
  return 1;
}

int main(int argc, char*argv[]){

   // Read "test.jpg" and only output every 8th row and every 8th column for a 64x size reduction
   read_JPEG_file ("test.jpg",8); // HACK
}

我编译它:

clang -std=c11  $(pkg-config --cflags --libs libjpeg) example.c -o example

推荐阅读