Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2 3 4  Все   Вниз
  Печать  
Автор Тема: Избавится от циклов при рисовании картинки в Qt  (Прочитано 86410 раз)
0 Пользователей и 1 Гость смотрят эту тему.
zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« : 14-05-2013 06:10 » 

Здравствуйте.

Я задавал много, где на форумах этот вопрос, но пока решения не получилось. Может быть здесь найдётся решение.

Задача:
У меня есть двоичный (бинарный) файл. То есть каждый байт файл это значение RGB.
R = 1-й байт
G = 1-й байт
B = 1-й байт
На следующей стадии цикла будет
R = 2-й байт
G = 2-й байт
B = 2-й байт
и так далее

Я пробовал заполнить объект QImage с помощью метода "setPixel" и выводить через метод "drawImage", так как этот метод позволяет выводить по частям.
Но увы для использования метода "setPixel" нужен двойной цикл, а значений очень много и из-за этого всё тормозит.

OpenGL использовать нельзя.

Может есть механизм без циклов заполнить объект QImage или какой-то другой механизм заполнения есть?

Мне нужно, что бы ни при заполнении, ни при выводе циклов не было,  при том вывод должен уметь выводить по частям.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 14-05-2013 06:14 » 

zuze, привет ) Я перенёс в общий, так как для Qt раздела у нас ещё нет, а тема не про Unix вовсе

Добавлено через 3 минуты и 12 секунд:
а по задаче - вроде всё уже выяснили неоднократно . Ты заполняешь QImage один раз (циклами или методом), потом пользуешься на здоровье.
« Последнее редактирование: 14-05-2013 06:17 от Алексей1153 » Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #2 : 14-05-2013 06:21 » 

Цитата
Добавлено через 3 минуты и 12 секунд:
а по задаче - вроде всё уже выяснили неоднократно . Ты заполняешь QImage один раз (циклами или методом), потом пользуешься на здоровье.

Циклами заполнить QImage это легко, но тормозит всё, а что же за метод который может без циклов заполнить?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #3 : 14-05-2013 06:22 » 

zuze, покажи цикл, который у тебя тормозит. Без всего лишнего
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #4 : 14-05-2013 06:26 » 

Алексей++, вот этот цикл жутко тормозит.

Код:
// Глобальные
int buffer[492][720];
QImage im(720, 492, QImage::Format_ARGB32);

Код:
for (int j = 0; j < 720; j++)
{
     for (int i = 491; i >= 0; i--)
     {
          buffer[i][j] = getc(fp);
          im.setPixel(j, i, QColor(buffer[i][j], buffer[i][j], buffer[i][j], 255).rgba());
      }
 }
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #5 : 14-05-2013 06:44 » 

zuze, есть метод

QImage::fromData ( const QByteArray & data, const char * format = 0 )

1) создаёшь экземпляр QByteArray
2) в цикле заполняешь его из источника
3) перетусовываешь байты под твой формат
4) вызываешь QImage::fromData

на мой взгляд это всё выполнится достаточно быстро
Записан

darkelf
Молодой специалист

no
Offline Offline

« Ответ #6 : 14-05-2013 07:12 » 

Алексей++, вот этот цикл жутко тормозит.

Код:
// Глобальные
int buffer[492][720];
QImage im(720, 492, QImage::Format_ARGB32);

Код:
for (int j = 0; j < 720; j++)
{
     for (int i = 491; i >= 0; i--)
     {
          buffer[i][j] = getc(fp);
          im.setPixel(j, i, QColor(buffer[i][j], buffer[i][j], buffer[i][j], 255).rgba());
     }
}
имхо, как вариант попробовать вычитать buffer, зараннее одним чтением fread(), правда тогда надо будет поменять int buffer[][] на unsigned char buffer[][] (или в файле хранить не байты, а int-ы), и поменять порядок обхода - тогда лучше будет работать кеши процессора:
Код:
fread(buffer, 1, sizeof(buffer), fp);
for (int i = 0; i < 492; i++)
{
     for (int j = 0; j < 720; j++)
     {
          im.setPixel(j, i, QColor(buffer[i][j], buffer[i][j], buffer[i][j], 255).rgba());
     }
}
« Последнее редактирование: 14-05-2013 07:44 от darkelf » Записан
zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #7 : 14-05-2013 07:14 » 

zuze, есть метод

QImage::fromData ( const QByteArray & data, const char * format = 0 )

1) создаёшь экземпляр QByteArray
2) в цикле заполняешь его из источника
3) перетусовываешь байты под твой формат
4) вызываешь QImage::fromData

на мой взгляд это всё выполнится достаточно быстро

А почему это убыстрит скорость, ведь "2) в цикле заполняешь его из источника", тоже значит циклы есть?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 14-05-2013 07:21 » 

darkelf, это кроссплатформа, не думай про кеши ) А вычитать можно и кусками побольше, это я согласен.

zuze, потому что QImage::setPixel - медленная операция . Ну и побайтное чтение из потока - тоже не айс, как выше правильно отметили )
Записан

darkelf
Молодой специалист

no
Offline Offline

« Ответ #9 : 14-05-2013 07:27 » 

Алексей++,  э.. ну если мы пишем на C/C++, а не на Java, то, по возможности, про кеши надо думать всегда, если интересно - есть статья Ульриха Дреппера "Что каждый программист должен знать о памяти" по этому поводу, если интересно - вот ссылка на её перевод на русский: http://rus-linux.net/MyLDP/hard/memory/memory.html, думаю, что она может оказаться полезной.

Кстати, по поводу конструктора - у QImage, если я правильно понял, есть ещё вариант:
Код:
QImage ( uchar * data, int width, int height, Format format );
т.е. можно,  сделать что-то типа такого:
Код:
fread(buffer, 1, sizeof(buffer), fp);
QImage (buffer, 720, 492, QImage::Format_ARGB32);
Хотя наверное надо будет буфер, как сказал Алексей++, ещё обработать..
« Последнее редактирование: 14-05-2013 07:45 от darkelf » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #10 : 14-05-2013 07:31 » 

darkelf, я гляну, но в данном случае это всё не имеет отношения к делу ИМХО )
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #11 : 15-05-2013 04:52 » 

zuze, потому что QImage::setPixel - медленная операция . Ну и побайтное чтение из потока - тоже не айс, как выше правильно отметили )

И что же в таких случаях используют? Скорее всего я не первый, кто с такой проблемой сталкивается. Может какие другие надо модули использовать.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #12 : 15-05-2013 11:26 » 

zuze, так уже снова всё расписали )) Прочитай!
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #13 : 17-05-2013 04:48 » 

zuze, так уже снова всё расписали )) Прочитай!

Прошу прощения, у нас сейчас переест вообще замаялся и не увидел Вашу строчку в конце "на мой взгляд это всё выполнится достаточно быстро".

Я понял, что надо сделать по Вашему алгоритму.

Цитата: Алексей++
QImage::fromData ( const QByteArray & data, const char * format = 0 )

1) создаёшь экземпляр QByteArray
2) в цикле заполняешь его из источника
3) перетусовываешь байты под твой формат
4) вызываешь QImage::fromData

Как будет готово всё покажу. Может кому тоже пригодится.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #14 : 17-05-2013 05:07 » 

ещё момент - если заранее знаешь, какой будет конечный объём QByteArray после чтения из файла , имеет смысл перед чтением вызвать

 QByteArray::reserve(конечный объём в байтах)

поскольку читать ты будешь  кусками, а не разом, это тоже чуток повысит скорость работы. Хотя, может и незначительно в данном случае. Попробуешь, в общем
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #15 : 17-05-2013 09:58 » 

Алексей++, вот у меня неожиданно появился вопрос по поводу заполнения QByteArray значениями из файла.

Пытался сделать так:

Код:
QByteArray b;

for (int k = 0; k < 492*720; k++)
     b[k] = getc(fp);

qDebug() << b.toInt();

fp определён так:

Код:
FILE *fp;

Выскакивает ошибка вообще, не из той области.
Ошибка такая: QColor::setRgb: RGB parameters out of range.

То есть данный код, повлиял на код который ниже расположен, а без этого кода ни какой ошибки нет.
Как же это поправить?

Я бы с удовольствием использовал в место FILE модуль QFile, но в нём возникает ошибка.

я делаю так:

Код:
chdir("file");

QFile fp("1.dat");

if (!fp.open(QIODevice::ReadOnly))
    qDebug() << "File not open";
   
QByteArray file;
file = fp.readAll();

qDebug() << file.toInt();

Выводиться 0 хотя в бинарном (двоичном) файле куча байт.
Если последнюю строчку заменяю на

Код:
qDebug() << file;

То выводятся, только первые десять байт.

Почему такие странные результаты вывода? Мне нужно, что бы выводились все значения из файла в десятичном виде.
« Последнее редактирование: 17-05-2013 12:09 от zuze » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #16 : 17-05-2013 16:22 » 

ты же место в массиве не выделил, а заполняешь - естественно всё падать будет. Но в Qt всё проще можно, и не нужно никаких FILE :

Код:
QFile file("путь");
if(file.open(file.ReadOnly)
{
    QByteArray data=file.readAll ();
    file.close();

    //работаем ...
}


Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #17 : 20-05-2013 05:20 » 

Алексей++, сделал по Вашему примеру аналогичный код.

Вот он:

Код:
chdir("file");
QFile file("1.dat");

if (file.open(file.ReadOnly))
{
   QByteArray data = file.readAll();
   file.close();
   qDebug() << data;
 }

В результате всё равно выводятся только первый десять байт. А на самом деле байт намного больше. Я посмотрел в редакторе двоичных (бинарных) файлов и увидел, что обрыв происходит на байте со значением "00".
Но я не могу поверить, что "readAll()" не может обработать значение байта "00", а может это, так "qDebug()" реагирует на байт со значением "00".
Что же всё таки произошло, почему не всё выводится?
« Последнее редактирование: 20-05-2013 05:27 от zuze » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #18 : 20-05-2013 05:44 » 

всё происходит правильно. Продолжай Улыбаюсь

размер

data.size()
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #19 : 20-05-2013 08:31 » 

всё происходит правильно. Продолжай Улыбаюсь

Код:
chdir("file");
QFile file("1.dat");

if (file.open(file.ReadOnly))
{
   file.seek(file.size() - (492*720));

   QByteArray mydata = file.readAll();
   file.close();

   im = QImage::fromData(mydata, QImage::Format_ARGB32);

   QPainter Tochka;
   Tochka.begin(this);

   Tochka.translate(50, 50);
   Tochka.drawImage(0, 0, im, 0, 0, 720, 492);

   Tochka.end();

Конечно перед строчкой "im = QImage::fromData(mydata, QImage::Format_ARGB32);" надо было сделать "перетусовывание байтов под формат", но я не совсем понял, как это сделать, это что надо было QByteArray надо использовать как двухмерный массив и возможно ли это.

Что касается строки:

Код:
file.seek(file.size() - (492*720));

Так это смещение, так как первые 8 байт это управляющие байты.

im объявлена глобально:

Код:
QImage im(720, 492, QImage::Format_ARGB32);

Но в результате выскакивает ошибка явно не с тем, что я не "перетусовал байты под формат".
Ошибка такая:
conversion from 'qbytearray' to 'const uchar*' is ambiguous
candidates are: QByteArray::operator QNotImplicitBoolCast() const <near match>
QByteArray::operator const void*() const <near match>
QByteArray::operator const char*() const <near match>

Ошибка указывает на строку:

Код:
im = QImage::fromData(mydata, QImage::Format_ARGB32);
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #20 : 20-05-2013 08:58 » 

Код: (C++)
        if (file.open(file.ReadOnly))
        {
                /*
                строка
                           file.seek(file.size() - (492*720));
                у тебя ничего не делает и вообще не нужна. Две цифры - вообще непонятно откуда )
               
                откуда инфа про 8 служебных байтов ? Где структура, которая их описывает ?
               
                пропустить размер в 8 байтов проще удалением их из загруженного массива
                */

               
                QByteArray mydata = file.readAll();
                file.close();
                mydata.remove(0,8/*sizeof(та самая структура с описанием)*/);
               
                /*сильно предполагаю, что в этой структуре и размеры сидят*/
                int xsize=...;
                int ysize=...;
               

                //здесь переформатирование
                //...
       
               
/*
If format is not specified (which is the default), the loader probes the file for a header to determine the file format. If format is specified, it must be one of the values returned by QImageReader::supportedImageFormats().
*/

                QImage::Format f=QImage::Format_ARGB32;
                im = QImage::fromData(mydata, &f);
               
               
   }
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #21 : 20-05-2013 09:37 » 

откуда инфа про 8 служебных байтов ?

Это из условия которое мне сказали, когда дали файл.

Где структура, которая их описывает ?

По горизонтали 720, а по вертикали 492. Заполняется картинка с лева на право с низу вверх.
То есть, пока xsize = 0,   int ysize меняется от 0 до 491. Сначала берётся первые 492 байта, затем следующие 492 байта и так далее.

Добавлено через 23 минуты и 14 секунд:
Алексей++, сделал вроде как Вы написали:

Код:
chdir("file");
QFile file("1.dat");

if (file.open(file.ReadOnly))
{
   QByteArray mydata = file.readAll();
   file.close();

   mydata.remove(0, 8);

   // здесь переформатирование (Пока не понимаю, как сделать)

   QImage::Format f = QImage::Format_ARGB32;
    im = QImage::fromData(mydata, &f);

    QPainter Tochka;
    Tochka.begin(this);

    Tochka.translate(50, 50);

    Tochka.drawImage(0, 0, im, 0, 0, 720, 492);

    Tochka.end();

Таже самая ошибка и на той же строчке кода. В чём же дело?
Можно ли обратится к QByteArray, как к двухмерному массиву? Если да, то я без проблем сделаю "переформатирование".
« Последнее редактирование: 20-05-2013 10:00 от zuze » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #22 : 20-05-2013 10:34 » 

а, это я невнимательно поспотрел. Для получения указателя на начад\ло буфера данных надо вызвать метод

data()

но, блин, это из справки можно было уже и так понять ))

перейти к "двухмерности" - только вручную.  К примеру, сначала в массиве структур S идёт первая строка, потом вторая, тогда индекс структуры со строкой row и столбцом col будет равен

Код:
uint32_t i=row*Cols+col
, где Cols - размер строки (то есть количество колонок)


Код:
S* pArrayOfS=(S*) mydata.data();
uint32_t i=5*Cols+7 ;
pArrayOfS[i]; //это ссылка на элемент S с нужным индексом (5,7)



собственно, S - это структура, описывающая один пиксел. Для конвертации будет удобно использовать похожую структуру с переставленными битовыми полями
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #23 : 20-05-2013 12:16 » 

Для получения указателя на начад\ло буфера данных надо вызвать метод

data()

Как я понял, надо в место:

Код:
im = QImage::fromData(mydata, &f);

написать:

Код:
im = QImage::fromData(mydata.data(), &f);

Но это приводит к ошибке, а именно:
invalid conversion from 'char*' to 'const uchar*'
initializing argument 1 of 'static QImage QImage::fromData(const uchar *, int, const char*)'
invalid conversion from 'QImage::Format*' to 'int'
initializing argument 2 of 'static QImage QImage::fromData(const uchar *, int, const char*)'

В добавок если я начну "переформатирование" в двухмерный массив, то я уже не смогу воспользоваться методом fromData(), который необходим для повышение скорости.
Может есть какое другое "переформатирование" которое бы в конкретном варианте подошло?
Записан
darkelf
Молодой специалист

no
Offline Offline

« Ответ #24 : 20-05-2013 13:36 » 

zuze, как вариант, попробуйте написать
Код:
im = QImage::fromData((const uchar*)mydata.data(), &f)
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #25 : 20-05-2013 15:44 » 

zuze, ты можешь получить неконстантный указатель на буфер mydata - data(). Через него ты меняешь элементы к нужному виду (это я и назвал переформатированием), не меняя размер буфера (и, естественно, не залазя за его границы!). Потом этот объект mydata и используешь в методе QImage::fromData

Цитата
QImage   fromData ( const QByteArray & data, const char * format = 0 )
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #26 : 21-05-2013 05:36 » 

zuze, как вариант, попробуйте написать
Код:
im = QImage::fromData((const uchar*)mydata.data(), &f)

Попробовал, ошибки остались, но уменьшились, остались только:
invalid conversion from 'QImage::Format*' to 'int'
initializing argument 2 of 'static QImage QImage::fromData(const uchar *, int, const char*)'

Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #27 : 21-05-2013 05:41 » 

zuze, это потому, что там ещё длину массива надо передать. (Ты прототипы функций то гляди)

а я предлагаю использовать метод, где передаётся сразу ссылка на QByteArray
Записан

zuze
Опытный

ru
Offline Offline
Пол: Мужской
Россия, Москва


« Ответ #28 : 21-05-2013 05:53 » 

zuze, это потому, что там ещё длину массива надо передать. (Ты прототипы функций то гляди)

а я предлагаю использовать метод, где передаётся сразу ссылка на QByteArray

Я не могу понять, выбираю "QImage::fromData(const uchar *, const char*)", а при компиляции запускается "QImage::fromData(const uchar *, int, const char*)", почему же так происходит?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #29 : 21-05-2013 05:57 » 

выбираю "QImage::fromData(const uchar *, const char*)",
такой перегрузки нет. Есть только две:

Код:
QImage fromData ( const uchar * data, int size, const char * format = 0 )
QImage fromData ( const QByteArray & data, const char * format = 0 )
Записан

Страниц: [1] 2 3 4  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines