Comment déterminer les contours d’une image de manière optimale?

J’ai récemment été confronté au problème du recadrage et du redimensionnement des images. J’avais besoin de rogner le “contenu principal” d’une image, par exemple si j’avais une image semblable à celle-ci: alt text http://soffr.miximages.com/c%23/

le résultat devrait être une image avec le contenu msn sans les marges blanches (gauche et droite).

Je recherche sur l’axe X le premier et le dernier changement de couleur et sur l’axe Y la même chose. Le problème est que traverser l’image image par ligne prend un certain temps … pour une image de 2000x1600px, il faut jusqu’à 2 secondes pour renvoyer les données CropRect => x1, y1, x2, y2.

J’ai essayé de faire pour chaque coordonnée un parcours et de m’arrêter sur la première valeur trouvée, mais cela ne fonctionnait pas dans tous les cas de test. Parfois, les données renvoyées n’étaient pas celles attendues et la durée des opérations était similaire.

Une idée de la réduction du temps de parcours et de la découverte du rectangle autour du “contenu principal”?

public static CropRect EdgeDetection(Bitmap Image, float Threshold) { CropRect cropRectangle = new CropRect(); int lowestX = 0; int lowestY = 0; int largestX = 0; int largestY = 0; lowestX = Image.Width; lowestY = Image.Height; //find the lowest X bound; for (int y = 0; y < Image.Height - 1; ++y) { for (int x = 0; x  Threshold)) { if (lowestX > x) lowestX = x; if (largestX  Threshold)) { if (lowestY > y) lowestY = y; if (largestY < y) largestY = y; } } } if (lowestX  0 ? lowestX - 3 : 0; else cropRectangle.X = 0; if (lowestY  0 ? lowestY - 3 : 0; else cropRectangle.Y = 0; cropRectangle.Width = largestX - lowestX + 8 > Image.Width ? Image.Width : largestX - lowestX + 8; cropRectangle.Height = largestY + 8 > Image.Height ? Image.Height - lowestY : largestY - lowestY + 8; return cropRectangle; } } 

Une optimisation possible consiste à utiliser Lockbits pour accéder aux valeurs de couleur directement plutôt que via GetPixel, beaucoup plus lent.

Si vous recherchez Lockbits, le premier résultat est http://www.bobpowell.net/lockingbits.htm . C’est une bonne référence.

D’autre part, mes tests ont montré que les frais généraux associés à Lockbits ralentissent cette approche si vous essayez d’écrire un équivalent de GetPixelFast équivalent à GetPixel et de le déposer en remplacement. Au lieu de cela, vous devez vous assurer que tous les access aux pixels sont effectués en une seule frappe plutôt que plusieurs. Cela devrait correspondre parfaitement à votre code, à condition de ne pas verrouiller / déverrouiller chaque pixel.

Voici un exemple

 BitmapData bmd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, b.PixelFormat); byte* row = (byte*)bmd.Scan0 + (y * bmd.Ssortingde); // Blue Green Red Color c = Color.FromArgb(row[x * pixelSize + 2], row[x * pixelSize + 1], row[x * pixelSize]); b.UnlockBits(bmd); 

Deux autres choses à noter:

  1. Ce code est dangereux car il utilise des pointeurs
  2. Cette approche dépend de la taille des pixels dans les données Bitmap. Vous devrez donc déduire pixelSize de bitmap.PixelFormat.

GetPixel est probablement votre principal coupable (je vous recommande d’exécuter des tests de profilage pour le localiser), mais vous pouvez restructurer l’algorithme de la manière suivante:

  1. Balayez la première ligne (y = 0) de gauche à droite et de droite à gauche et enregistrez le premier et le dernier emplacement du bord. Il n’est pas nécessaire de vérifier tous les pixels, car vous voulez les bords extrêmes.
  2. Parcourez toutes les lignes suivantes, mais maintenant, il ne rest plus qu’à effectuer une recherche vers l’extérieur (du centre vers les bords), en commençant par le dernier bord minimum connu. Nous voulons trouver les limites extrêmes, nous n’avons donc besoin que de chercher dans la région où nous pourrions trouver de nouveaux extrema.
  3. Répétez les deux premières étapes pour les colonnes, en établissant les extrema initiaux, puis en les utilisant pour lier la recherche de façon itérative.

Cela devrait réduire considérablement le nombre de comparaisons si vos images sont généralement principalement du contenu. Le pire des cas est une image totalement vierge, ce qui serait probablement moins efficace que la recherche exhaustive.

Dans les cas extrêmes, le traitement de l’image peut également tirer parti du parallélisme (division de l’image et traitement de celle-ci en plusieurs threads sur un processeur multicœur), mais cela demande un peu plus de travail et vous permet d’apporter d’autres modifications plus simples. Les frais généraux de threading ont tendance à limiter l’applicabilité de cette technique et sont surtout utiles si vous prévoyez d’exécuter cette opération en temps réel, avec traitement répété des données entrantes (pour compenser les coûts de configuration initiaux).

Cela ne rendra pas la chose meilleure à la commande … mais si vous atteignez votre seuil, vous n’aurez pas besoin de faire une racine carrée, ce qui est très coûteux.

Cela devrait donner une augmentation significative de la vitesse.