Bereiche
News
Rewind
Tipps & Berichte
Forum
Galerie
Journals
Events
Umfragen
Themenwoche
Kleinanzeigen
Interaktiv
Anmelden
Registrierung
Zu allen empfangenen Nachrichten
Suche...
Zur erweiterten Suche
Push-Nachrichten von MacTechNews.de
Würden Sie gerne aktuelle Nachrichten aus der Apple-Welt direkt über Push-Nachrichten erhalten?
Forum
>
Entwickler
>
NSImage Durchschnittsfarbe (GCD?)
NSImage Durchschnittsfarbe (GCD?)
Christoph_M
31.03.12
02:27
Moin,
ich versuche gerade an meinen 27" iMac eine Art "Self Made Ambilight" ranzubauen.
Habe bereits die LEDs hinten dran und kann dieser per Arduino ansteuern.
Die zweite Komponente ist eine Cocoa App welche von 7 Blöcken am rechten Bildschirmrand und 7 Blöcken am Linken Rand (jeweils 20% rein) die Durchschnittsfarbe ausrechnet.
Das klappt auch schon super (siehe Screenshot, wobei die Ergebnisse derzeit nur in der Konsole, nicht auf der GUI landen).
Mein Problem ist, dass die Berechnung zu lange dauert (0,5 bis 1 Sekunde) und ich mich frage ob das nicht optimierbar ist, indem z.B. GCD genutzt wird.
Derzeit schaue ich nur jeden 2. Pixel in jeder 2. Reihe an, hier würde auch weniger gehen, aber ich würde vorher lieber erst meinen Code optimieren.
Hier ist mein Code:
for(int x=xStart;x<xEnde;x=x+2) {
for(int y=yStart;y<yEnde;y=y+2) {
color = [bitmapRep colorAtX:x y:y];
sumRed += color.redComponent;
sumGreen += color.greenComponent;
sumBlue += color.blueComponent;
counter++;
if(y==yStart || yEnde-y < 2) // Das ist nur zur Visualisierung drin
[bitmapRep setColor:[NSColor blueColor] atX:x y:y];
else
[bitmapRep setColor:[NSColor redColor] atX:x y:y];
}
}
xStart,xEnde,yStart und yEnde markieren jeweils Anfang und Ende des Blockes (die Blocks sieht man sehr gut im Screenshot).
Im Moment laufe ich das Ganze in einer extra GCD Queue durch. Hatte auch schon getestet für jeden Block eine eigene Queue zu verwenden, das ganze wurde dadurch aber sogar langsamer.
Da der Prozessor irgendwo bei 80% rumdümpelt, frage ich mich, wie ich mehr aus der Hardware (i7) holen kann.
Irgendwelche Ideen?
Danke schonmal und viele Grüße,
Christoph
p.s. wenn das Projekt fertig ist mach ich ein Journal
Hilfreich?
0
Kommentare
Marcel Bresink
31.03.12
12:01
Erzwinge, dass Mac OS X aus dem gegebenen NSImage (oder NSImageRep) eine NSBitmapImageRep mit gegebenen Parametern rendert und lese die Pixel dann als rohe Bytes, durch Zugriff auf die -bitmapData. Das wird um viele Größenordnungen schneller sein und der Zugriff auf nur jedes zweite Pixel kann man sich schenken.
Das Rendern kann man hinkriegen, indem man eine leere NSBitmapImageRep mit den gewünschten Werten anlegt und dann das Bild mithilfe eines [NSGraphicsContext graphicsContextWithBitmapImageRep: x] und einem -drawInRect: dort hineinzeichnet.
Noch was anderes: Ein beliebter Fehler ist es, die Durchschnittsfarbe nach dem Motto (r+g+b)/3 zu berechnen. Das darf man nicht so machen, da das Ergebnis dann violettstichig wird.
Hilfreich?
0
Marcel Bresink
31.03.12
13:13
Nachtrag: Den letzten Absatz bitte streichen. Ich hatte schon weitergedacht und die Farbwerte in Luminanz und Chrominanz getrennt. Für Christophs Vorhaben ist das unnötig.
Hilfreich?
0
Christoph_M
31.03.12
21:49
Hi Marcel,
vielen Dank für deine schnelle Antwort!
Ich hatte mich schon gefreut, weil das Bild sowieso als NSBitmapImageRep vorliegt
Allerdings muss ich sagen dass ich mich mit allen C Konstrukten nicht auskenne, hab also erstmal sowas versucht (bitmapRep ist mein Screenshot):
unsigned short* destData = (unsigned short*) [bitmapRep bitmapData];
for(int i=0;i<sizeof(destData);i++) {
NSLog(@"%c",destData[i]);
}
da kommt dann aber nur sowas:
2012-03-31 21:06:25.164 MacAmbilight[23771:403] ı
2012-03-31 21:06:25.165 MacAmbilight[23771:403] ˇ
2012-03-31 21:06:25.165 MacAmbilight[23771:403] Ù
Habe jetzt noch versucht die 14 Blöcke per dispatch_apply(14, queue, ^(size_t idx){...
auszuführen, allerdings ist dort die Laufzeit doppelt so lange wie bei einem einfachen dispatch_sync.
grüße,
Christoph
Hilfreich?
0
Marcel Bresink
01.04.12
11:09
Ich hatte mich schon gefreut, weil das Bild sowieso als NSBitmapImageRep vorliegt
Das nützt nichts, weil Du im Vorhinein nicht weißt, wie das Bild im Speicher intern abgelegt ist. Das kann je nach Quelle des Bildes, verwendeter Grafikkarte, usw. ganz verschieden sein. Du musst Mac OS X dazu zwingen, das Bild in einem von Dir definierten Layout im Speicher abzulegen, z.B. 8 Bit pro Pixel in der Reihenfolge rot-grün-blau-alpha (RGBA). Dazu muss das Bild nochmals intern gerendert werden.
da kommt dann aber nur sowas:
Der Code enthält zwei Fehler. Zum einen liegen die Daten in den seltensten Fällen als 16-Bit-Werte (unsigned short) vor, zum anderen läuft die Schleife nur über die Größe des Pointers. Mit anderen Worten, auf einem 32-Bit-Rechner wird die Schleife 4-mal, bei 64 Bit acht Mal durchlaufen.
Wenn ich Zeit habe, kann ich mal eine richtige Lösung posten.
Hilfreich?
0
Marcel Bresink
01.04.12
11:16
Im letzten Beitrag muss es "8 Bit pro Farbkomponente, 32 Bit pro Pixel" heißen.
Hilfreich?
0
Marcel Bresink
02.04.12
14:08
Hier eine mögliche Quick-and-Dirty-Lösung aus der Mittagspause:
NSArray *inputReps = [NSBitmapImageRep imageRepsWithContentsOfFile: @"/PathToFile"];
NSBitmapImageRep *sourceRep, *scanRep;
NSGraphicsContext *bitmapContext;
NSInteger width, height, x, y, pixelCount;
unsigned char *p, *pLine;
unsigned long long rSum, gSum, bSum;
struct timeval before, after;
sourceRep = [inputReps objectAtIndex: 0];
width = [sourceRep pixelsWide];
height = [sourceRep pixelsHigh];
scanRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
pixelsWide: width
pixelsHigh: height
bitsPerSample: 8
samplesPerPixel: 4
hasAlpha: YES
isPlanar: NO
colorSpaceName: NSCalibratedRGBColorSpace
bytesPerRow: 0
bitsPerPixel: 0];
bitmapContext = [NSGraphicsContext graphicsContextWithBitmapImageRep: scanRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext: bitmapContext];
[sourceRep drawInRect: NSMakeRect(0.0, 0.0, (CGFloat)width, (CGFloat)height)];
[NSGraphicsContext restoreGraphicsState];
pLine = (unsigned char*)[scanRep bitmapData];
rSum = 0;
gSum = 0;
bSum = 0;
pixelCount = 0;
gettimeofday(&before, NULL);
for (y = 0; y < height; y++) {
p = pLine;
for (x = 0; x < width; x++) {
rSum += *p++;
gSum += *p++;
bSum += *p++;
p++;
pixelCount++;
}
pLine += [scanRep bytesPerRow];
}
gettimeofday(&after, NULL);
[scanRep release];
NSLog(@"Average color: (%f, %f, %f) (%lld pixels)\n", (double)rSum / 256.0 / (double)pixelCount,
(double)gSum / 256.0 / (double)pixelCount,( double)bSum / 256.0 / (double)pixelCount,
(long long)pixelCount);
NSLog(@"Time: %f s\n", after.tv_sec - before.tv_sec + (after.tv_usec - before.tv_usec) / 1000000.0);
Um
sämtliche
Pixel des angesprochenen 3,6 Megapixel-Bildes zu berücksichtigen, braucht das Programm auf meinem Rechner gerade einmal 0,04 Sekunden. Auf einem einzelnen Prozessor, ohne GCD-Parallelisierung.
Hilfreich?
0
qbert
02.04.12
15:26
Klingt für mich irgendwie aufwendig die Pixel
alle
auszulesen. Kannst Du nicht mit der Grafikkarte erst mal einen Filter über den Screenshot laufen lassen, der das Bild runterskaliert/blurt damit der Arbeitsaufwand kleiner wird?
Hilfreich?
0
Christoph_M
12.04.12
22:36
Hallo Marcel,
ich wollte mich nur kurz melden um mich für das Codeschnipsel zu bedanken!
Ich bin leider noch nicht dazugekommen es auszutesten weil ich beruflich kurzfristig weg musste, aber das Dankeschön sei dir trotzdem schon gewiss!
Meine Erfahrungen poste ich dann hier oder vielleicht eröffne ich auch ein Journal.
Beste Grüße,
Christoph
Hilfreich?
0
Kommentieren
Diese Diskussion ist bereits mehr als 3 Monate alt und kann daher nicht mehr kommentiert werden.
iOS 18.2, macOS 15.2 und Co.: Apple stopft viel...
Apple-Leak spricht vom "iPad Air M3"
iOS 18.3 mit Hinweisen auf neue Apple-Hardware
Gurman zum Release des neuen Apple TV, HomePods...
Vor 18 Jahren: iPhone, Apple TV und "Apple Inc."
Test Marantz Model 60n
TechTicker
Apple kündigt Systemupdates für heute Abend an ...