Qt - wczytywanie i operowanie na bitmapach za pomocą klasy QImage
Stronę tą wyświetlono już: 683 razy
Wydawać się może to dość dziwne, że informacje na temat klasy QImage służącej do obsługi bitmap opisuję w dziale dotyczącym programowania konsolowego. Podjąłem jednak taką decyzję, gdyż w ten sposób mogę pokazać podstawy operacji możliwych do wykonania na bitmapach bez konieczności posiadania GUI.
Konfiguracja projektu
Ponieważ klasa QImage domyślnie jest przeznaczona dla projektów GUI, więc aby dostęp do pliku nagłówkowego QImage był możliwy konieczna jest edycja pliku z rozszerzeniem .pro, który jest dostępny w każdym projekcie w oknie pokazanym na poniższym rysunku.

Po dwukrotnym kliknięciu (w moim przypadku pozycji ImageExample.pro) oczom twym powinien ukazać się następujący kod:
- QT += core
- QT -= gui
- CONFIG += c++11
- TARGET = ImageExamples
- CONFIG += console
- CONFIG -= app_bundle
- TEMPLATE = app
- SOURCES += main.cpp
Gdzie linijkę 2 należy zamienić na:
- QT += gui
aby możliwe było korzystanie z klasy QImage.
Domyślnie obsługiwane formaty plików
Klasa QImage umożliwia odczyt/zapis plików następujących formatów:
Format | Opis | Obsługa |
---|---|---|
BMP | nieskompresowane bitmapy | odczyt/zapis |
GIF | Graphic Interchange Format | odczyt |
JPG | Joint Photographic Experts Group | odczyt/zapis |
JPEG | Joint Photographic Experts Group | odczyt/zapis |
PNG | Portable Network Graphics | odczyt/zapis |
PBM | Portable Bitmap | odczyt |
PGM | Portable Graymap | odczyt |
PPM | Portable Pixmap | odczyt/zapis |
XBM | X11 Bitmap | odczyt/zapis |
XPM | X11 Pixmap | odczyt/zapis |
Odczyt bitmapy z pliku i podstawowe jego parametry
Jak już wspomniałem klasa QImage umożliwia odczyt różnych formatów plików zawierających obraz rastrowy. Taką możliwość wczytania danych daje sam konstruktor klasy QImage:
- #include <QCoreApplication>
- #include <QDebug>
- #include <QImage>
- int main(int argc, char *argv[])
- {
- QCoreApplication a(argc, argv);
- QImage image("fog.jpg");
- qDebug() << "Rozmiar bitmapy:" << image.size();
- qDebug() << "Głębia kolorów:" << image.depth();
- return a.exec();
- }
W powyższym kodzie odczytany został plik fog.jpg oraz wyświetlona została informacja o jego rozmiarze i głębi (liczbie bitów na piksel). Niestety najwyraźniej klasa QImage zamienia 24 bitową bitmapę automatycznie przy wczytywaniu na 32 bitową (czyli z kanałem alfa).
Wynik działania powyższego kodu:
Rozmiar bitmapy: QSize(600, 400) Głębia kolorow: 32
Dostęp do pikseli bitmapy
Po wczytaniu bitmapy można pozyskać informację o kolorze danego jej piksela. Do tego celu służy metoda pixel, oto przykład jej użycia:
- int x = 100;
- int y = 100;
- QRgb color = image.pixel(x, y);
- qDebug() << "Kolor z pozycji" << x << ";" << y << ":" << QString("czerwony = %1; zielony = %2; niebieski = %3; alfa = %4").arg(qRed(color)).arg(qGreen(color)).arg(qBlue(color)).arg(qAlpha(color));
Wynik działania powyższego kodu:
Kolor z pozycji 100 ; 100 : "czerwony = 198; zielony = 178; niebieski = 145; alfa = 255"
Możliwe jest również ustawienie koloru danego piksela bitmapy za pomocą metody setPixel:
- int x = 100;
- int y = 100;
- QRgb color = image.pixel(x, y);
- qDebug() << "Kolor z pozycji" << x << ";" << y << ":" << QString("czerwony = %1; zielony = %2; niebieski = %3; alfa = %4").arg(qRed(color)).arg(qGreen(color)).arg(qBlue(color)).arg(qAlpha(color));
- image.setPixel(x, y, qRgba(0,0,0,255));
- color = image.pixel(x, y);
- qDebug() << "Kolor z pozycji" << x << ";" << y << ":" << QString("czerwony = %1; zielony = %2; niebieski = %3; alfa = %4").arg(qRed(color)).arg(qGreen(color)).arg(qBlue(color)).arg(qAlpha(color));
Wynik działania powyższego kodu:
Kolor z pozycji 100 ; 100 : "czerwony = 198; zielony = 178; niebieski = 145; alfa = 255" Kolor z pozycji 100 ; 100 : "czerwony = 0; zielony = 0; niebieski = 0; alfa = 255"
Operacje na pikselach za pomocą powyższych metod niestety są bardzo powolne, dlatego można też operować na poszczególnych pikselach bitmapy wykorzystując metodę bits, która zwraca wskaźnik do tablicy typu uchar (unsigned char) reprezentującej składowe kolorów pikseli bitmapy w układzie: czerwony, zielony, niebieski, alfa. Oto przykład wykorzystania tego wskaźnika do rozjaśnienia kolorów bitmapy:
- #include <QCoreApplication>
- #include <QDebug>
- #include <QImage>
- void minmax(uchar* value, int add){ // normalizacja wartości by nie przekraczała zakresu 0 - 255
- *value = (int)*value + add < 255 ? ((int)*value + add > 0 ? *value + add : 0) : 255;
- }
- int main(int argc, char *argv[])
- {
- QCoreApplication a(argc, argv);
- QImage image("fog.jpg", "jpg");
- uchar* bits = image.bits(); // pozyskuję wskaźnik na początek tablicy bitów
- uchar* bitsEnd = bits + image.byteCount(); // obliczam wskaźnik na koniec tablicy
- int add = 50; // dodawana wartość
- for(uchar* index = bits; index < bitsEnd; index += 4){
- minmax(index, add); // składowa czerwona
- minmax(index + 1, add); // składowa zielona
- minmax(index + 2, add); // składowa niebieska
- }
- image.save("fog2.jpg");
- return a.exec();
- }
Wynik działania powyższego kodu na przykładowej bitmapie pokazany został na poniższym rysunku.


Zapis obrazu do pliku
Obraz można również zapisać do pliku za pomocą metody save, która w przypadku niektórych formatów umożliwia również ustawienie poziomu kompresji:
- image.save("fog2.jpg"); // z domyślną kompresją
- image.save("fog3.jpg", "jpg", 0); // z najniższą możliwą kompresją
- image.save("fog4.jpg", "jpg", 100); // z największą możliwą kompresją