English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
ساحة المشروع الفعلية: إزالة خلفية الصورة البيضاء النقية للحصول على صورة شفافة تستخدم في وظيفة الجمع.
مقدمة إلى ثلاث طرق للمعالجة من خلال الطرق الثلاث (لا أعرف لماذا تذكرت كونغ يي جي)، الأداء الحقيقي لم يكن مقارنًا، إذا كان هناك رجل عظيم يمكنه إخباري، سيكونني ممتنًا جدًا.
Core Image Core Graphics/Quarz 2D Core Image
Core Image هو إطار عمل قوي جدًا. يمكنه السماح لك بتطبيق أنواع مختلفة من المرشحات بسهولة لمعالجة الصور، مثل تعديل بريق الصورة، اللون، أو التشويه. يستخدم GPU (أو CPU) لمعالجة بيانات الصور والكادرات من الفيديو بسرعة كبيرة، حتى حقيقة الوقت. ويخفي جميع التفاصيل الداخلية للمعالجة البصرية، حيث يمكن الاستفادة بسهولة من واجهة البرمجة المقدمة دون الحاجة إلى معرفة كيفية استخدام OpenGL أو OpenGL ES لاستغلال قدرات GPU، ولا تحتاج إلى معرفة كيفية عمل GCD في ذلك، حيث يقوم Core Image بمعالجة جميع التفاصيل.
في المستندات الرسمية لشركة Appleدليل برمجة Core Imageتم ذكروصفة مرشح الألوان الداكنةبالنسبة لمثال معالجة الخلفية
استخدم نموذج اللون HSV، لأن نموذج اللون HSV، في التعبير عن نطاق اللون، أكثر لذة من نموذج اللون RGB
عملية المعالجة بشكل عام:
يتم إنشاء ملف تعريف نقاط التكامل من أجل إزالة نطاق القيم اللونية من ملف النسيج مكعب، يتم تعيين Alpha لللون المستهدف إلى 0.0f باستخدام مرشح CIColorCube وcubeMap للمعالجة اللونية للصورة الأصلية للحصول على CIImage المعالجة من قبل CIColorCube، يتم تحويلها إلى CGImageRef في نظام Core Graphics، يتم الحصول على الصورة الناتجة باستخدام imageWithCGImage:
ملاحظة: في الخطوة الثالثة، لا يمكن استخدام imageWithCIImage: مباشرة، لأنه ليس UIKit UIImage معياري، وإذا تم استخدامه مباشرة، قد يحدث عدم العرض
- (UIImage *)removeColorWithMinHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle image:(UIImage *)originalImage{ CIImage *image = [CIImage imageWithCGImage:originalImage.CGImage]; CIContext *context = [CIContext contextWithOptions:nil]; // kCIContextUseSoftwareRenderer : CPURender /** ملاحظة * UIImage يتم إنشاؤه باستخدام CIimage، فإنه ليس UIKit UIImage عبر CGImage المعيارية * لذا إذا لم يتم استخدام السياق للرسم المعالجة، فإنه لا يمكن عرضه بشكل صحيح */ 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 size = 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"]; // Set data for cube [colorCube setValue:data forKey:@"inputCubeData"]; [colorCube setValue:originalImage forKey:kCIInputImageKey]; CIImage *result = [colorCube valueForKey:kCIOutputImageKey]; return result; } struct CubeMap createCubeMap(float minHueAngle, float maxHueAngle) { const unsigned int size = 64; struct CubeMap map; map.length = size * size * size * sizeof (float) * 4; map.dimension = size; float *cubeData = (float *)malloc (map.length); float rgb[3], hsv[3], *c = cubeData; for (int z = 0; z < size; z++){ rgb[2] = ((double)z)/(size-1); // قيمة الأزرق for (int y = 0; y < size; y++){ rgb[1] = ((double)y)/(size-1); // قيمة الأصفر for (int x = 0; x < size; x ++){ rgb[0] = ((double)x)/(size-1); // قيمة الأحمر rgbToHSV(rgb,hsv); // استخدم قيمة اللون لتحديد ما يجب جعله شفافًا // يحدد الحد الأدنى والحد الأعلى لزاوية اللون اعتمادًا على // اللون الذي تريد إزالته float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f: 1.0f; // حساب القيم المضروبة مسبقًا للألفا للكرة c[0] = rgb[0] * alpha; c[1] = rgb[1] * alpha; c[2] = rgb[2] * alpha; c[3] = alpha; c += 4; // التقدم في البوابة إلى القيمة التالية لللون في الذاكرة } } } map.data = cubeData; return map; }
لم يتم ذكر rgbToHSV في الوثائق الرسمية، لكنني وجدت معالجة التحويل في مدونة الكاتب المذكور في النص أدناه. شكرًا لكم.
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; }
لنحاول الآن إزالة تأثير الخلفية الخضراء، وكيف سيبدو ذلك
يمكننا من خلال استخدامأداة HSV, لتحديد نطاق تقريبي للHUE الأخضر بين 50-170
تجربة دعوة هذه الطريقة
[[SPImageChromaFilterManager sharedManager] removeColorWithMinHueAngle:50 maxHueAngle:170 image:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"nb" ofType:@"jpeg"]]]
النتيجة.
يبدو أن التأثير جيدًا نوعًا ما.
إذا نظرت بدقة إلى نموذج HSV، ربما لاحظت أننا لا نستطيع التأثير على الألوان الرمادية والبيضاء والسوداء من خلال تحديد زاوية اللون (Hue)، وعلينا استخدام درجة التشبع (Saturation) والنقاوة (Value) معًا لل�断، يمكن للطلاب المهتمين البحث عن هذا في الكود�断Alpha float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f: 1.0f;واختبار النتائج هناك. (وفيما يتعلق بالتحويل بين RGB وHSV في الكود، يرجى البحث عن تحويلاتهم على Baidu، لأن الكاتب ليس على دراية بهم. اه، الكاتب لا يعرف ماذا يقول.)
إذا كنت مهتمًا بـ Core Image، فالرجاء الانتقال إلى سلسلة المقالات الخاصة بالسيد الكبير
iOS8 Core Image In Swift: تحسين الصورة تلقائيًا واستخدام المرشحات المدمجة
iOS8 Core Image In Swift: مرشحات أكثر تعقيدًا
iOS8 Core Image In Swift: اكتشاف الوجه وتشويه الصورة
iOS8 Core Image In Swift: مرشحات الفيديو الحي
Core Graphics/Quarz 2D
الذين ذُكرت في النص المسبق Core Image مبنية على OpenGL قوية جدًا، كأساس آخر للرؤيةCore Graphicsمثل القوة. خلال استكشافه، تعرف الكاتب على معلومات الصور. لذا، يُقترح أن يتم جمعها هنا للاستشارة في المستقبل.
إذا لم تكن مهتمًا بالاستكشاف، فجفو إلى الجزء الأخير من المقال Masking an Image with Color
Bitmap
في وثائق Quarz 2D الرسمية، بالنسبة لـوصف BitMap كالتالي:
صورة bitmap (أو صورة عينية) هي مجموعة من البكسل (أو العينات). يمثل كل بكسل نقطة واحدة في الصورة. ملفات الصور الجرافيكية JPEG، TIFF، و PNG هي أمثلة على صور bitmap.
تنسيقات بكسل 32-bit و 16-bit للألوان CMYK و RGB في Quarz 2D
العودة إلى احتياجاتنا، بالنسبة لاستبعاد اللون المحدد في الصورة، إذا كان بإمكاننا قراءة معلومات RGBA لكل بكسل، وال�断اء من قيمها، إذا كانت تتوافق مع نطاق الهدف، سنقوم بتعديل قيمة Alpha الخاصة به إلى 0، ثم نخرجها كصورة جديدة، وسنتمكن من تنفيذ طريقة معالجة cubeMap المذكورة في النص المسبق.
قدمنا لنا Quarz 2D القوية القدرة على تنفيذ هذه العملية، والآن يرجى النظر في مثال الرمز أدناه:
- (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{ // تخصيص الذاكرة متغير imageWidth = image.size.width; متغير imageHeight = image.size.height; size_t bytesPerRow = imageWidth * 4; uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight); // 创建context CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // 色彩范围的容器 CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage); // 遍历像素 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; } printf("\n---->ptr0:%d ptr1:%d ptr2:%d ptr3:%d<----\n",ptr[0],ptr[1],ptr[2],ptr[3]); } } // 将内存转成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]; // تحرير CGImageRelease(imageRef); CGContextRelease(context); CGColorSpaceRelease(colorSpace); return resultUIImage; }
هل تتذكرون العيوب التي ذكرناها في نموذج HSV في Core Image؟ إذن، Quarz 2D يستخدم مباشرة معلومات RGBA للمعالجة، مما ي规避 مشكلة عدم الود مع الألوان السوداء والبيضاء، نحتاج فقط إلى تعيين نطاق RGB ( لأن الألوان السوداء والبيضاء في نموذج RGB يمكن تحديدها بشكل جيد)، يمكننا تعبئة ذلك بشكل تقريبي. مثلما يلي
- (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]; }
انظر إلى مقارنة تأثيرات معالجة الخلفية البيضاء التي قمنا بها
يبدو أن ذلك جيد، ولكن بالنسبة للملابس الشيفرة، يبدو غير ودود. انظر إلى بعض الصور التي قمت بتحليلها
بوضوح، إذا لم يكن هناك خلفية بيضاء، تأثير "الملابس المهترئة" سيكون واضحًا جدًا. هذا المشكلة، في الثلاثة طرق التي حاولت تجربتها، لم تجد من يحتاجها، إذا كان هناك أي شخص كبير يعرف طريقة جيدة للتعامل مع هذا، وكان يستطيع إخباري، سأكون ممتنًا جدًا. (أضع ركبتي هنا أولاً)
إضافة إلى المشكلة السابقة، القيم التي يتم قراءتها باستخدام هذه الطريقة المقارنة لكل بكسل قد تكون مختلفة عن القيم التي تم إنشاؤها أثناء الرسم. ولكن هذا الخطأ لا يمكن رؤيته بالعين المجردة.}}
في الشكل التالي، عندما نقوم بإنشاء الصورة، نضبط القيم RGB على 100/240/220، ولكن عند معالجتها باستخدام CG، القيم التي يتم قراءتها هي 92/241/220. مقارنة بالـ 'الجديد' و 'الحالي' في الصورة، لا يمكن رؤية الفرق في اللون تقريبًا. هذا المشكلة الصغيرة تعرفونها فقط، ولم يكن لها تأثير كبير على تأثير الإزالة الفعلي.
تغطية الصورة باللون
لقد جربت فهمًا واستخدام الطريقة السابقة، ثم وجدت هذه الطريقة عند قراءة الوثائق مرة أخرى، وكأنني وجدت هدية من أب Apple. دعونا نبدأ بالكود
- (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]; }
الخلاصة
نموذج اللون HSV أكثر فعالية من نموذج اللون RGB في إزالة الألوان من الصور، بينما النمط RGB يفعل العكس تمامًا. لأنني أحتاج فقط إلى إزالة الخلفية البيضاء في المشروع، اخترت النمط الأخير في النهاية.