проблемы с загрузкой изображений с python и gobject - PullRequest
7 голосов
/ 30 апреля 2011

У меня есть скрипт с интерфейсом GTK (GObject), который я использую для публикации в своем фото-блоге.

Я пытаюсь улучшить его отзывчивость, загружая изображения в фоновом потоке.

Мне не повезло, когда я пытался заполнить объекты GdkPixbuf из фонового потока, все, что я пробовал, просто заедает.

Так что в качестве альтернативы я подумал, что прочитал файлы в фоновом потоке, а затем вставил их в GdkPixbuf по запросу. Этот подход дал некоторые удивительные и довольно удручающие результаты производительности, которые заставляют меня задуматься, не делаю ли я что-то в корне неправильно.

Я играю со слегка сжатыми JPEG-файлами от моей камеры, они, как правило, составляют около 3,8 МБ.

Вот оригинальная блокировка загрузки изображения:

pb = GdkPixbuf.Pixbuf.new_from_file(image_file)

Это в среднем около 550 мс, не очень большое, но довольно утомительное, если вы хотите пролистать дюжину изображений.

Тогда я разделил это, вот файл, прочитанный:

data = bytearray(open(self.image_file).read())

Это в среднем 15 мс, это действительно приятно, но также немного волнует, если мы сможем прочитать файл за 15 мс, на что тратятся остальные 535 мс?

Между прочим, вызов bytearray существует, потому что PixBufLoader не принял бы данные в противном случае.

А затем нагрузка Pixbuf:

pbl = GdkPixbuf.PixbufLoader()
pbl.write(data, len(data))
pbl.close()
pb = pbl.get_pixbuf()

В среднем это около 1400 мс, что почти в 3 раза больше, чем позволяет Gtk делать все это.

Я что-то здесь не так делаю?

Ответы [ 2 ]

2 голосов
/ 03 мая 2011

Мое предположение: вы делаете что-то не так.Я только что сравнил libjpeg-turbo с gdk.PixbufLoader и практически не обнаружил различий в скорости.Код, который я использовал ниже:

Для libjpeg-turbo (jpegload.c):

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include <jpeglib.h>

void decompress(FILE* fd)
{
  JSAMPARRAY buffer;
  int row_stride;
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_decompress(&cinfo);
  jpeg_stdio_src(&cinfo, fd);
  jpeg_read_header(&cinfo, TRUE);
  jpeg_start_decompress(&cinfo);
  row_stride = cinfo.output_width * cinfo.output_components;
  buffer = (*cinfo.mem->alloc_sarray)
                ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
  while (cinfo.output_scanline < cinfo.output_height) {
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
  }
  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);
}

int main(int argc, char** argv)
{
  long len;
  FILE *fd;
  unsigned char *buf;
  struct timeval start, end;
  int i;
  const int N = 100;
  int delta;

  /* read file to cache it in memory */
  assert(argc == 2);
  fd = fopen(argv[1], "rb");
  fseek(fd, 0, SEEK_END);
  len = ftell(fd);
  rewind(fd);
  buf = malloc(len);
  assert(buf != NULL);
  assert(fread(buf, 1, len, fd) == len);

  gettimeofday(&start, NULL);
  for(i = 0; i < N; i++) {
    rewind(fd);
    decompress(fd);
  }
  gettimeofday(&end, NULL);
  if(end.tv_sec > start.tv_sec) {
    delta = (end.tv_sec - start.tv_sec - 1) * 1000;
    end.tv_usec += 1000000;
  }
  delta += (end.tv_usec - start.tv_usec) / 1000;
  printf("time spent in decompression: %d msec\n",
         delta/N);
}

Для Python GDK (gdk_load.py):

import sys
import gtk
import time

def decompress(data):
    pbl = gtk.gdk.PixbufLoader()
    pbl.write(data, len(data))
    pbl.close()
    return pbl.get_pixbuf()

data = open(sys.argv[1]).read()

N = 100
start = time.time()
for i in xrange(N):
    decompress(data)
end = time.time()
print "time spent in decompression: %d msec" % int((end - start) * 1000 / N)

Результаты теста:

$ gcc jpegload.c -ljpeg
$ ./a.out DSC_8450.JPG 
time spent in decompression: 75 msec
$ python gdk_load.py DSC_8450.JPG 
time spent in decompression: 75 msec
$ identify DSC_8450.JPG 
DSC_8450.JPG JPEG 3008x2000 3008x2000+0+0 8-bit DirectClass 2.626MB 0.000u 0:00.019

РЕДАКТИРОВАТЬ: и другой тест, используя gi.repostiroy на этот раз:

import sys
import time
from gi.repository import GdkPixbuf

def decompress(filename):
    pb = GdkPixbuf.Pixbuf.new_from_file(filename)
    return pb

N = 100
start = time.time()
for i in xrange(N):
    decompress(sys.argv[1])
end = time.time()
print "time spent in decompression: %d msec" % int((end - start) * 1000 / N)

И результаты:

$ python gi_load.py DSC_8450.JPG 
time spent in decompression: 74 msec

GdkPixbuf.PixbufLoader, использующий gi.repository, действительно НАМНОГО медленнее, чем «чистый» gtk.gdk.Код:

import sys
import time
from gi.repository import GdkPixbuf

def decompress(data):
    pbl = GdkPixbuf.PixbufLoader()
    pbl.write(data, len(data))
    pbl.close()
    return pbl.get_pixbuf()

data = bytearray(open(sys.argv[1]).read())

N = 100
start = time.time()
for i in xrange(N):
    decompress(data)
end = time.time()
print "time spent in decompression: %d msec" % int((end - start) * 1000 / N)

Результаты:

$ python gi_load.py DSC_8450.JPG 
time spent in decompression: 412 msec

Но GdkPixbuf.Pixbuf.new_from_file работает так же быстро, как чистая версия C, даже используя gi.repository, так что вы все равно делаете что-то не так или ожидаетеслишком много.

1 голос
/ 16 июня 2011

Я разработал небольшую программу просмотра изображений с pygtk.Я использую PixbufLoader, но я пишу только N байт на запись ().В сочетании с idle_add () я могу загрузить изображение в фоновом режиме, в то время как приложение все еще отвечает на ввод пользователя.

Вот источник: http://guettli.sourceforge.net/gthumpy/src/ImageCache.py

...