C++: Ursache für Lesezugriffsverletzung?

1 Antwort

Vom Fragesteller als hilfreich ausgezeichnet

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.

Woher ich das weiß:Berufserfahrung – Berufstätigkeit als Software-Entwickler
redsky 
Fragesteller
 03.07.2022, 21:47

Hi, danke schonmal für die Antwort.
Tatsächlich verstehe ich nicht wieso

sizeof(uint32)

zum Raster hinzugefügt wird. In der Doku steht:

The raster is assumed to be an array of width times height 32-bit entries

Da hätte ich gedacht width * height reicht auch. Allerdings bekomme ich dann eine Fehlermeldung bei:

TIFFReadRGBAImageOriented

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

TIFFReadRGBAImageOriented

den Faktor 4 braucht um die Werte in den raster zu schreiben, wieso sollte der Vector das nicht auch brauchen?

0
BorisG2011  03.07.2022, 23:28
@redsky

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.

1
BorisG2011  03.07.2022, 23:41
@BorisG2011

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..

1
redsky 
Fragesteller
 03.07.2022, 23:45
@BorisG2011

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!

1
redsky 
Fragesteller
 04.07.2022, 23:14
@BorisG2011

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.

0