English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Scène réelle du projet : Supprimer le fond blanc pur de l'image pour obtenir une image avec un fond transparent à utiliser pour la fonction de collage
Présente deux méthodes et trois modes de traitement (je ne sais pourquoi cela me fait penser à Kong Yiji), mais je n'ai pas comparé les performances. Si quelqu'un peut me donner des informations, je serais très reconnaissant.
Core Image Core Graphics/Quarz 2D Core Image
Core Image est un cadre très puissant. Il vous permet d'appliquer simplement divers filtres pour traiter les images, comme modifier la brillance, la couleur ou l'exposition. Il utilise le GPU (ou le CPU) pour traiter rapidement, voire en temps réel, les données d'image et les frames de vidéo. Et il cache tous les détails du traitement graphique sous-jacent, permettant une utilisation simple via l'API fournie, sans se soucier de la manière dont OpenGL ou OpenGL ES utilise pleinement les capacités du GPU, ni de la manière dont GCD joue son rôle. Core Image traite tous les détails.
Dans les documents officiels d'Apple :Guide de programmation Core ImageDans :Recette de filtre Chroma KeyPour un exemple de traitement de l'arrière-plan :
L'utilisation du modèle de couleur HSV, car le modèle HSV est plus convivial pour représenter une gamme de couleurs par rapport au modèle RGB.
Processus général :
Créer une texture cubique cubeMap pour supprimer une gamme de couleurs, régler l'alpha de la couleur cible à 0.0f, utiliser le filtre CIColorCube et le cubeMap pour traiter la couleur de l'image source, obtenir l'objet CIImage traité par CIColorCube, le convertir en objet CGImageRef de Core Graphics via imageWithCGImage :, et obtenir l'image finale
Attention : Ne pas utiliser directement imageWithCIImage :, car le résultat n'est pas une UIImage standard. Si l'on utilise directement cela, il peut y avoir des problèmes de non-affichage.
- (UIImage *)removeColorWithMinHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle image:(UIImage *)originalImage{ CIImage *image = [CIImage imageWithCGImage:originalImage.CGImage]; CIContext *context = [CIContext contextWithOptions:nil];// kCIContextUseSoftwareRenderer : CPURender /** Note * The UIImage initialized through CIimage is not a standard UIImage like CGImage. * Therefore, if the rendering process is not performed using context, it is impossible to display normally. */ CIImage *renderBgImage = [self outputImageWithOriginalCIImage:image minHueAngle:minHueAngle maxHueAngle:maxHueAngle]; CGImageRef renderImg = [context createCGImage:renderBgImage fromRect:image.extent]; UIImage *renderImage = [UIImage imageWithCGImage:renderImg]; return renderImage; } struct CubeMap { int length; float dimension; float *data; }; - (CIImage *)outputImageWithOriginalCIImage:(CIImage *)originalImage minHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle{ struct CubeMap map = createCubeMap(minHueAngle, maxHueAngle); const unsigned int taille = 64; // Create memory with the cube data NSData *data = [NSData dataWithBytesNoCopy:map.data length:map.length freeWhenDone:YES]; CIFilter *colorCube = [CIFilter filterWithName:@"CIColorCube"]; [colorCube setValue:@(size) forKey:@"inputCubeDimension"]; // Définir les données pour le cube [colorCube setValue:data forKey:@"inputCubeData"]; [colorCube setValue:originalImage forKey:kCIInputImageKey]; CIImage *resultat = [colorCube valueForKey:kCIOutputImageKey]; return resultat; } struct CubeMap createCubeMap(float minHueAngle, float maxHueAngle) { const unsigned int taille = 64; struct CubeMap carte; longueurCarte = taille * taille * taille * tailleOctet (float) * 4; dimensionCarte = taille; float *cubeData = (float *)malloc (longueurCarte); float rgb[3], hsv[3], *c = cubeData; for (int z = 0; z < taille; z++{ rgb[2] = ((double)z)/(taille-1); // Valeur bleue for (int y = 0; y < taille; y++{ rgb[1] = ((double)y)/(taille-1); // Valeur verte for (int x = 0; x < taille; x ++{ rgb[0] = ((double)x)/(taille-1); // Valeur rouge rgbToHSV(rgb,hsv); // Utilisez la valeur de teinte pour déterminer ce qui doit être transparent // L'angle de teinte minimum et maximum dépend de // la couleur que vous souhaitez supprimer float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle)63; 0.0f: 1.0f; // Calculer les valeurs d'alpha prémultipliées pour le cube c[0] = rgb[0] * alpha; c[1] = rgb[1] * alpha; c[2] = rgb[2] * alpha; c[3] = alpha; c += 4; // avancer notre pointeur en mémoire pour la prochaine valeur de couleur } } } map.data = cubeData; return map; }
rgbToHSV n'est pas mentionné dans la documentation officielle, l'auteur a trouvé le traitement de conversion pertinent dans le blog de l'expert mentionné ci-dessous. Merci
void rgbToHSV(float *rgb, float *hsv) { float min, max, delta; float r = rgb[0], g = rgb[1], b = rgb[2]; float *h = hsv, *s = hsv + 1, *v = hsv + 2; min = fmin(fmin(r, g), b ); max = fmax(fmax(r, g), b ); *v = max; delta = max - min; if( max != 0 ) *s = delta / max; else { *s = 0; *h = -1; return; } if( r == max ) *h = ( g - b ) / delta; else if( g == max ) *h = 2 + ( b - r ) / delta; else *h = 4 + ( r - g ) / delta; *h *= 60; if( *h < 0 ) *h += 360; }
Essayons ensuite de voir comment se débrouille l'effet de suppression du fond vert
Nous pouvons utiliserOutil HSV, déterminer approximativement la gamme de valeurs HUE pour le vert50-170
Appelons cette méthode pour essayer
[[SPImageChromaFilterManager sharedManager] removeColorWithMinHueAngle:50 maxHueAngle:170 image:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"nb" ofType:@"jpeg"]]]
L'effet
L'effet semble tout à fait acceptable.
Si l'on observe attentivement le modèle HSV, on peut constater que nous sommes impuissants face à la spécification des tons neutres, gris et noirs en utilisant la méthode de spécification de l'angle de teinte (Hue). Nous devons utiliser la saturation (Saturation) et la luminosité (Value) ensemble pour le juger, les étudiants intéressés peuvent le voir dans le code.évaluer Alpha float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) &63; 0.0f: 1.0f;Là, essayez l'effet. (Quant à pourquoi RGB et HSV sont convertis ainsi dans le code, veuillez rechercher leurs conversions sur Baidu, car l'auteur paresseux ne les comprend pas. Ah, l'auteur paresseux est désespéré)
Si vous êtes intéressé par Core Image, veuillez consulter la série d'articles des grands auteurs
iOS8 Core Image In Swift : amélioration automatique des images et utilisation des filtres intégrés
iOS8 Core Image In Swift : filtres plus complexes
iOS8 Core Image In Swift : détection des visages et mosaïque
iOS8 Core Image In Swift : filtres vidéo en temps réel
Core Graphics/Quarz 2D
Comme mentionné précédemment, Core Image basé sur OpenGL est très puissant, en tant que l'autre fondation de la vue,Core GraphicsEgalement puissant. Son exploration a permis à l'auteur paresseux de mieux comprendre les connaissances liées aux images. Donc, ici, nous faisons un résumé pour une consultation future.
Si vous n'êtes pas intéressé par l'exploration, sautez directement à la partie Masquage d'une image avec une couleur à la fin de l'article
Bitmap
Dans Quarz 2Dans la documentation officielle de D, pourBitMap a la description suivante:
Une image bitmap (ou image échantillonnée) est un tableau de pixels (ou d'échantillons). Chaque pixel représente un point unique dans l'image. Les fichiers graphiques JPEG, TIFF et PNG sont des exemples d'images bitmap.
32-et 16-formats de pixel en bits pour les espaces de couleur CMYK et RGB dans Quartz 2D
Retournons à nos besoins, pour supprimer la couleur spécifique d'une image, si nous pouvons lire les informations RGBA de chaque pixel, les juger séparément, s'ils correspondent à la gamme cible, nous changeons leur valeur Alpha en 0, puis nous sortons une nouvelle image, ainsi nous avons réalisé une méthode similaire à celle de cubeMap mentionnée dans l'article précédent.
Quarz puissant 2D nous fournit la capacité d'effectuer cette opération, veuillez consulter l'exemple de code ci-dessous :
- (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{ // Allouer la mémoire const int imageWidth = image.size.width; const int imageHeight = image.size.height; size_t bytesPerRow = imageWidth * 4; uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight); // Créer le contexte CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();// Conteneur de plage de couleurs CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage); // Parcourir les pixels int pixelNum = imageWidth * imageHeight; uint32_t* pCurPtr = rgbImageBuf; for (int i = 0; i < pixelNum; i++, pCurPtr++) { uint8_t* ptr = (uint8_t*)pCurPtr; if (ptr[3] >= minR && ptr[3] <= maxR && ptr[2] >= minG && ptr[2] <= maxG && ptr[1] >= minB && ptr[1] <= maxB) { ptr[0] = 0; }else{ printf("\n---->ptr0:%d ptr1:%d ptr2:%d ptr3:%d<----\n",ptr[0],ptr[123] } } // Convertir la mémoire en image CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, nil); CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight,8, 32, bytesPerRow, colorSpace,kCGImageAlphaLast |kCGBitmapByteOrder32Little, dataProvider,NULL,true,kCGRenderingIntentDefault); CGDataProviderRelease(dataProvider); UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef]; // Liberation CGImageRelease(imageRef); CGContextRelease(context); CGColorSpaceRelease(colorSpace); return resultUIImage; }
Souvenez-vous des inconvénients du mode HSV mentionnés dans Core Image ? Alors Quartz 2D utilise directement les informations RGBA pour traiter, ce qui évite bien les problèmes d'inamicalité pour les couleurs noires et blanches. Nous n'avons qu'à définir la gamme RGB (car les couleurs noires et blanches peuvent être bien déterminées dans le mode de couleur RGB), nous pouvons donc encapsuler cela approximativement. Voici
- (UIImage *)removeWhiteColorWithImage:(UIImage *)image{ return [self removeColorWithMaxR:255 minR:250 maxG:255 minG:240 maxB:255 minB:240 image:image]; }
- (UIImage *)removeBlackColorWithImage:(UIImage *)image{ return [self removeColorWithMaxR:15 minR:0 maxG:15 minG:0 maxB:15 minB:0 image:image]; }
Regardez les effets de traitement par rapport au fond blanc que nous avons
Il semble plutôt bien, mais pour les vêtements en tissu, il n'est pas très amical. Regardez les tests de groupes d'images que j'ai faits
Il est évident que si le fond n'est pas blanc, l'effet "vêtements élimés" est très prononcé. Ce problème, dans les trois méthodes que j'ai essayées, n'a pas été évité. Si quelqu'un connaît une bonne méthode de traitement et peut l'expliquer à l'ignorant, je serais très reconnaissant. (Mets deux genoux ici)
En plus des problèmes mentionnés ci-dessus, la méthode de comparaison de chaque pixel, les valeurs lues sont sujettes à des erreurs par rapport à la création du graphique. Mais cette erreur est pratiquement invisible à l'œil nu.
Comme dans l'image suivante, nous avons défini les valeurs RGB suivantes lors de la création du graphique100/240/220 Mais en lisant les valeurs lues par le traitement de CG ci-dessus, elles sont92/241/220. Comparaison des images 'nouveau' et 'actuel', il est difficile de voir la différence de couleur. Ce petit problème, vous le savez, n'affecte pas grand-chose à l'effet de décoloration réel
Masquage d'une image avec une couleur
J'ai essayé de comprendre et d'utiliser la méthode précédente, puis j'ai découvert cette méthode en relisant le document, c'est comme si j'avais reçu une grâce de Father Apple. Voici le code
- (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{ const CGFloat myMaskingColors[6= {minR, maxR, minG, maxG, minB, maxB}; CGImageRef ref = CGImageCreateWithMaskingColors(image.CGImage, myMaskingColors); return [UIImage imageWithCGImage:ref]; }
Résumé
Le mode de couleur HSV est plus avantageux que le mode RGB pour supprimer les couleurs des images, tandis que RGB est tout à fait le contraire. En raison du projet, je n'ai besoin que de supprimer le fond blanc, donc j'ai finalement adopté cette dernière méthode.