16-Bit Graustufenbild mit QT lesen

Schwachkopp

Lt. Junior Grade
Registriert
Jan. 2016
Beiträge
307
Hallo,

bin auf ein Problem gestoßen, bei dem ich nicht weiterkomme. Ein 16-Bit Graustufenbild (Link zum Testbild) soll ausgelesen werden, wobei jedem Grauton eine Zahl zwischen 0 und 1 zugewiesen werden soll. Die Frage ist nun warum ich nur verschiedene 256 Grautöne erhalte obwohl die Datei eigentlich 16-Bit also ~65k Farbstufen hat.

Kann sich bitte jemand den untenstehenden Code und das oben verlinkte selbsterzeuge Testbild anschauen?

C++:
#include <QDebug>
#include <QImage>

QPixmap pix("test.png");
QImage img = pix.toImage();
img = img.convertToFormat(QImage::Format_Grayscale16);
for (int x = 0 ; x < 4096; x++) {
    // h[y] enthält Werte zwischen 0 und 2^16 - 1
    const uint16 *h = reinterpret_cast<const uint16*>(img.scanLine(x));
    for (int y = 0; y < 4096; y++) {
        qDebug() << x << y << h[y] / pow(2, 16);
    }
}
 
Zuletzt bearbeitet: (Link zu Direktdownloadlink geändert)
Sicher, dass deine Quelle auch tatsächlich ein 16 Bit Grayscale Image ist? Pixelwerte vor dem Konvertieren schon mal angeschaut?
 
DaysShadow schrieb:
Sicher, dass deine Quelle auch tatsächlich ein 16 Bit Grayscale Image ist? Pixelwerte vor dem Konvertieren schon mal angeschaut?
Nein, sicher bin ich nicht. Ich hätte dazu schreiben sollen, dass mein Wissen über Bildformate sehr begrenzt ist. Deshalb wäre es auch gut wenn sich jemand das Testbild anschaut.

Was ich getan habe, ist das Bild in Gimp zuladen - Gimp sagt 16-Bit Grayscale (siehe unten) - und einen Gaußian Blurfilter darauf anzuwenden und das dann nochmal abzuspeichern. Ich hatte gehofft, dass der Blurfilter zusätzliche Graustufen einführt falls die im Ursprungsbild nicht vorhanden waren. Es hat allerdings nicht geholfen.

1635504314711.png
 
Photoshop zeigt das es ein Graustufenbild mit 16-Bit ist. Ich würde sagen das du nur 8-Bit rausbekommst und daraus auch nur 256 Graustufen.
 
Aber worin unterscheidet sich dann ein 8-bit Bild von einem 16-bit Bild? Oder meinst du, dass es zwar im 16-bit Format gespeichert wurde, jedoch nur 8-bit Informationen enthält? Dann wäre das Bild das Problem.
 
Wahrscheinlich dein Script
 
Das hat eine 16bit Farbtiefe. Solltest du auch mit QImage::format() checken können.
Ich vermute das "Problem" spontan bei scanline.
In der Doku steht z.B.: "The scanline data is as minimum 32-bit aligned." klingt für mich so, als wenn dein cast nach 16 bit inkorrekt ist.
 
BeBur schrieb:
Das hat eine 16bit Farbtiefe. Solltest du auch mit QImage::format() checken können.
Ich vermute das "Problem" spontan bei scanline.
In der Doku steht z.B.: "The scanline data is as minimum 32-bit aligned." klingt für mich so, als wenn dein cast nach 16 bit inkorrekt ist.
QImage::format() sagt "QImage::Format_Grayscale16", was aber kein Wunder ist denn ich habe ja explizit in dieses Format konvertiert. Wenn ich das Bild pixelweise lese mit img.pixelColor(QPoint(x,y)) erhalte ich dasselbe Ergebnis.
Ergänzung ()

kartoffelpü schrieb:
Scheinen sogar nur 202 Graustufen drin zu sein. Sagt sowohl https://townsean.github.io/canvas-pixel-color-counter/ und ein Plugin in Paint.Net.
Ah danke! Sehr nützliche Website. Dann ist das Bild nicht geeignet. Werde nun versuchen ein echten 16-bit Bild zu finden.
 
Im Klartext ist quasi kein Bildformat. Wir haben früher immer PGM (Portable GrayMap genutzt, da das wirklich Klartext ist.
 
  • Gefällt mir
Reaktionen: Schwachkopp
Schwachkopp schrieb:
QImage::format() sagt "QImage::Format_Grayscale16", was aber kein Wunder ist denn ich habe ja explizit in dieses Format konvertiert
Nur allgemein: Dann ruft man die Funktion auf, bevor man das im Code konvertiert, wenn man wissen möchte wie das Eingangsformat ist ;)
 
  • Gefällt mir
Reaktionen: Schwachkopp und BeBur
Schwachkopp schrieb:
Nach der Zeile "QImage img = pix.toImage()"?
Ja. Da hast du es im Programm eingelesen, als Image vorliegen und hast noch nichts verändert.
 
  • Gefällt mir
Reaktionen: Schwachkopp
Imo gibt scanline ein unsigned char* zurück mit RGB-werten.
Ich persönlich würde 4096 nicht fix als Ende setzen, sondern mal schauen, wie viele Bytes so ein scanline denn beansprucht bzw. wie groß das h da denn eigentlich ist.
 
  • Gefällt mir
Reaktionen: Schwachkopp
BeBur schrieb:
Imo gibt scanline ein unsigned char* zurück mit RGB-werten.
Ich persönlich würde 4096 nicht fix als Ende setzen, sondern mal schauen, wie viele Bytes so ein scanline denn beansprucht bzw. wie groß das h da denn eigentlich ist.
Da hast du sicher recht. Ich habe das Scanline auch nur so benutzt, weil ich das irgendwo im Internet so gefunden hatte.

DaysShadow schrieb:
Ja. Da hast du es im Programm eingelesen, als Image vorliegen und hast noch nichts verändert.
Dann gibt es immer "QImage::Format_RGB32" aus. Muss nochmal genauer in die Dokumentation schauen, schätz ich.
 
Schwachkopp schrieb:
Da hast du recht. Ich habe das Scanline auch nur so benutzt, weil ich das irgendwo im Internet so gefunden hatte.
Dann hast du ja deinen Fehler vermutlich gefunden. Aus dem Internet abschreiben ist häufig keine gute Idee. Der erste Blick sollte immer in die Doku gehen.
 
  • Gefällt mir
Reaktionen: andy_m4 und Schwachkopp
BeBur schrieb:
Dann hast du ja deinen Fehler vermutlich gefunden. Aus dem Internet abschreiben ist häufig keine gute Idee. Der erste Blick sollte immer in die Doku gehen.
Nicht so voreilig. ;) Aber ja, ich sollte und werde das auf jeden Fall bald mal austesten.
 
  • Gefällt mir
Reaktionen: BeBur
Es ist immer gut, auch mal mit anderen Methoden in die Daten zu schauen, daher mein Senf:
A) Dein Bild von ganz oben:
schwachkopp.png: 4096x4096,1c, type= uint16
min=5962, max=57580, distinct value count 51073
B) Das Bild weiter drunter im Link:
linkbild.png: 309x412,3c, type= uint8
min=0, max=255, distinct value count 256

In Python/OpenCV:
Python:
import numpy as np
import cv2

fname = 'schwachkopp.png'
image = cv2.imread(fname, cv2.IMREAD_UNCHANGED)
channels = (image.shape[-1]-1) * (len(image.shape)>2) + 1
print('{:s}: {:d}x{:d},{:d}c, type='.format(fname, image.shape[0],image.shape[1],channels),image.dtype)
vmin, vmax = np.amin(image), np.amax(image)
unique, counts = np.unique(image.flatten(), return_counts=True)
print('min={:d}, max={:d}, distinct value count {:d}'.format(vmin,vmax,len(unique)))
 
  • Gefällt mir
Reaktionen: Schwachkopp
OpenCV wollte ich auch schon mal in mein Projekt einbinden aber mich schreckt der Aufwand ab. :)

BeBur schrieb:
Bei mir werden 57580 verwendete Graustufen angezeigt in Matlab.
blöderidiot schrieb:
distinct value count 51073
kartoffelpü schrieb:
Scheinen sogar nur 202 Graustufen drin zu sein.
Ok nun haben wir schon drei Möglichkeiten und ich bin leider immer noch nicht viel weiter. Die QImage-Methode bytesPerLine() sagt es wäre 8192 Bytes, d.h. es sind 2 Bytes bzw. 16 Bit zur Darstellung eines Farbwertes, weil ja die Bildgröße 4096x4096 beträgt. Also hat der obenstehende Code durch die zwei (4096er) For-Schleifen und den Cast zu uint16 alle Daten gelesen.

So nun kommt der Teil der mich wundert. Jeweils zwei Bytes sind paarweise gleich und ich verstehe nicht warum (siehe Code unten). Ich würde doch darauf tippen, dass etwas mit der Bilddatei nicht stimmt aber eure Antworten widersprechen dem teilweise.

Die Dokumentation hat mich auch nicht weitergebracht. Bin vermutlich zu blöd diese zu verstehen.

C++:
#include <QDebug>
#include <QImage>

QPixmap pix("test.png");
QImage img = pix.toImage();
img = img.convertToFormat(QImage::Format_Grayscale16);
for (int x = 0 ; x < 4096; x++) {
    uchar *h = img.scanLine(x);
    for (int y = 0; y < 2*4096; y = y + 2) {
        // h[y] == h[y + 1] für alle Schleifendurchläufe
        qDebug() << h[y] << h[y + 1];
    }
}
 
Zuletzt bearbeitet:
Zurück
Oben