C++: Ursache für Lesezugriffsverletzung?
Hallo,
ich komme an einer Stelle nicht weiter und könnte noch einmal Hilfe gebrauchen. Ich nutze die Bibliothek Libtiff um die Farbwerte von einem Bild in einen Vector zu speichern bekomme aber eine Lesezugriffsverletzung. Anbei der Code:
void GetImage(TIFF* tif)
{
uint32 npixels = width * height * sizeof(uint32);
uint32* raster;
std::vector <uint32> image;
raster = (uint32*)_TIFFmalloc(npixels);
if (TIFFReadRGBAImageOriented(tif, width, height, raster, ORIENTATION_TOPLEFT, 0) == 1)
{
std::cout << npixels << std::endl;
for (uint32 i = 0; i < npixels; i++)
{
image.push_back(raster[i]);
}
std::cout << image[0] << std::endl;
};
}
TIFFReadRGBAImageOriented
speichert die Pixelwerte von einem Bild in die Variable "raster" mit der Größe von "npixels". Ich möchte diese Werte später verwenden und sie deshalb global abspeichern. Deshalb habe ich den Vector
std::vector <uint32> image;
deklariert und möchte mit der for-Schleife die Werte von raster in image kopieren.
Ich bekomme leider folgende Fehlermeldung:
Ausgelöste Ausnahme: Lesezugriffsverletzung
std::forward<unsigned int const & __ptr64>(...) hat 0x22A6E503000 zurückgegeben.
npixels = 482241600. Wenn ich bei der for-Schleife anstelle von npixels 48224160 schreibe (also eine 0 weglasse), bekomme ich keine Fehlermeldung mehr. "raster" scheint also mehr Werte zu haben, als man in den Vector "image" hineinschreiben kann. Zumindest interpretiere ich das Problem so.
Ist meine Interpretation richtig und wenn ja, was kann ich tun um die Werte von raster irgendwie global abzuspeichern.
Viele Grüße
1 Antwort
Es geht hier um ein RGBA-Bild. Wenn ich es recht verstehe, sind die drei Farbwerte und der Alphawert in einem uint32-Wert zusammengefasst.
Danach wird der Speicherbedarf für die Variable raster durch die folgende Anweisung berechnet.
uint32 npixels = width * height * sizeof(uint32);
Das ist der Speicherbedarf in Bytes für ein Bild mit
width*height
Pixeln. Der Ausdruck sinzeof(uint32) fügt dem Produkt width*height den Faktor 4 hierzu, weil ein uint32 4 Bytes belegt. npixels gibt also nicht die Zahl der Pixel des Bildes an, sondern das vierfache davon.
Ich schlage vor, in der Schleife zu schreiben:
for (uint32 i = 0; i < width*height; i++) {
....
}
Anders gesagt: Es ist nicht erforderlich, den Wert 482241600 durch 10 zu teilen (durch Weglassen einer 0), aber das Teilen durch 4 könnte schon helfen.
Wir müssen unterscheiden
- zwischen der Größe des Bildes in Pixeln
- der Größe des mit _TIFFmalloc zu resservierenden Speichers. Die Funktion _TIFFmalloc erwartet die Angabe der Speichergröße in Bytes
- dem Datentyp der Elemente des Vektors
Das Bild hat width*height Pixel. Für jedes Pixel werden Rotwert, Grünwert, Blauwert und Alphawert in einer Zahl vom Typ uint32 zusammengefasst. Rotwert, Grünwert, Blauwert und Alphawert belegen je ein Byte, eine Zahl vom Typ uint32 belegt 4 Bytes. Deshalb ist es möglich, die vier Farbwerte in eine vorzeichenlose Ganzzahl zu packen.
In der Doku steht:
The raster is assumed to be an array of width times height 32-bit entries
Genau das drückt die Anweisung
raster = (uint32*)_TIFFmalloc(width*height*sizeof(uint32));
aus: Das Argument von _TIFFmalloc ist die erforderliche Größe des Speicherbereichs in Bytes, der cast (uint32*) erinnert daran, dass wir den Speicherbereich als Array von uint32-Zahlen interpretieren wollen. Das Array hat dann also width*height Elemente, weil ein uint32 gerade 4Bytes groß ist. Die Arrayindices laufen natürlich von 0 bis width*height - 1.
Ich würde vorschlagen, den Code so umzuschreiben, dass der Unterschied zwischen Bildgröße in Pixeln und Speicherbedarf in Bytes ganz deutlich wird:
uint32 npixels = width * height;
uint32 sizeOfMemoryBlock = npixels * sizeof(uint32);
// Pro Pixel werden 4 Bytes für RGBA-Werte benötigt
uint32* raster;
std::vector <uint32> image;
raster = (uint32*)_TIFFmalloc(sizeOfMemoryBlock );
if (TIFFReadRGBAImageOriented(tif, width, height,
raster, ORIENTATION_TOPLEFT, 0) == 1)
{
std::cout << npixels << std::endl;
// Jetzt werden alle Elemente des Arrays raster
// in einen Vektor komiert
for (uint32 i = 0; i < npixels; i++)
{ // Jeder Durchlauf kopiert eines uint32-Werte,
// als 4 Bytes
image.push_back(raster[i]);
}
std::cout << image[0] << std::endl;
};
Noch eine Anmerkung zu TIFFReadRGBAImageOriented: Du schreibst:
Wenn
TIFFReadRGBAImageOriented
den Faktor 4 braucht um die Werte in den raster zu schreiben, wieso sollte der Vector das nicht auch brauchen?
TIFFReadRGBSImageOriented interpretiert raster als Array von uint32-Werten, dies ergibt sich aus der Vereinbarung
uint32* raster;
Das ist nämlich der Zeiger auf ein Array, dessen Elemente den Typ uint32 haben. Dieses Array hat wie gesagt width*height Elemente und der Vektor hat, sobald er vollständig geschrieben ist, ebenfalls wdith*height Elmente.. Der Faktor 4 wird einzig und allein für den Aufruf von _TIFFmalloc benötigt. Mein Vorschlag, im Code zwischen npixels und sizeOfMemoryBlock zu unterscheiden, verdeutlicht diesen Sachverhalt auch.
Noch eine Anmerkung zum Schluss: Es erscheint mir nicht erforderlich, das Array, auf das über den Zeiger raster zugegriffen wird, in einen Vektor zu kopieren. Mit einem Array kannst du alles machen, was du auch mit einem Vektor machen kannst.
Nachtrag:
In dem von dir genannten Tutorial steht übrigens:
uint32 npixels=width*h;
raster=(uint32 *) _TIFFmalloc(npixels *sizeof(uint32));
Du hast das leider nicht getreulich abgeschrieben, sondern daraus gemacht:
uint32 npixels = width * height * sizeof(uint32);
raster = (uint32*)_TIFFmalloc(npixels);
Dabei hast du den Faktor sizeof(uint32) von der Stelle, an der er erforderlich ist, verschoben an eine Stelle, an die er nicht hingehört und an der er für die Zahl der Pixel des Bildes einen falschen Wert verursacht..
oh wow, das macht tatsächlich einen Unterschied wo ich das
*sizeof(uint32)
hinschreibe... Vielen Dank schonmal für die ausführliche Antwort! Das muss ich mir dann morgen umbedingt nochmal ansehen!
Jaaa ok ich verstehe den Unterschied zwischen npixels und _TIFFmalloc(npixels * sizeof(uint32)). "raster" hat tatsächlich nicht die Anzahl der Pixel des Bildes sondern die Speichergröße die benötigt wird um diese Pixel zu speichern. Zusätzlich ist raster noch ein Zeiger auf das Array mit den Bilddaten in Byte.
Jetzt macht es auch Sinn wieso
* sizeof(uint32)
nicht npixels zugewiesen werden darf.
Ich habe durchaus ein wenig Erfahrung in Python aber mit so Sachen wie Speicherplatz zuweisen und Zeigern auf Arrays... da tue ich mir noch schwer. Das ist nochmal eine Stufe abstrakter.
Hi, danke schonmal für die Antwort.
Tatsächlich verstehe ich nicht wieso
zum Raster hinzugefügt wird. In der Doku steht:
Da hätte ich gedacht width * height reicht auch. Allerdings bekomme ich dann eine Fehlermeldung bei:
Ich habe das sizeof(uint32) nur wegen diesem Tutorial überhaupt reingeschrieben.
Ich bekomme keine Fehlermeldung, wenn ich deine For-Schleife verwende, allerdings habe ich dann Sorge, dass nicht alle Pixel in meinen Vector geschrieben werden, sondern nur ein Teil. Wenn
den Faktor 4 braucht um die Werte in den raster zu schreiben, wieso sollte der Vector das nicht auch brauchen?