2010-12-06 11 views
5

J'ai écrit un programme qui prend une «photo» et pour chaque pixel qu'il choisit d'insérer une image parmi une gamme d'autres photos. L'image choisie est la photo dont la couleur moyenne est la plus proche du pixel original de la photographie. Je l'ai fait en faisant d'abord la moyenne des valeurs rgb de chaque pixel dans l'image 'stock' puis en le convertissant en CIE LAB afin que je puisse calculer à quel point il est proche du pixel en question en termes de perception humaine de la couleur.Ajout à un fichier image

J'ai ensuite compilé une image dans laquelle chaque pixel de l'image originale «photo» a été remplacé par l'image boursière «la plus proche». Cela fonctionne bien et l'effet est bon mais la taille de l'image stock est de 300 par 300 pixels et même avec les drapeaux de machine virtuelle de "-Xms2048m -Xmx2048m", qui oui je sais est ridiculus, sur 555px par 540px image I ne peut remplacer les images stockées à 50 px avant d'avoir une erreur de mémoire insuffisante.

Donc, fondamentalement, j'essaie de penser à des solutions. Tout d'abord je pense que l'effet d'image lui-même peut être amélioré en faisant la moyenne de chaque pixel de 4 pixels (2x2 carrés) de l'image originale en un seul pixel puis en remplaçant ce pixel par l'image. . Cela devrait également me permettre de dessiner les images de stock à une plus grande taille. Quelqu'un at-il une expérience dans ce genre de manipulation d'image? Si oui, quelles astuces avez-vous découvert pour produire une belle image. Finalement, je pense que la façon de réduire les erreurs de mémoire consisterait à enregistrer plusieurs fois l'image sur le disque et à ajouter la ligne d'images suivante au fichier tout en supprimant continuellement l'ancien ensemble d'images rendues de la mémoire. Comment cela peut-il être fait? Est-ce similaire à l'ajout d'un fichier normal?

Toute aide dans ce dernier domaine serait grandement appréciée.

Merci,

Alex

Répondre

3

Je suggère de regarder dans l'API Java Advanced Imaging (JAI). Vous utilisez probablement BufferedImage en ce moment, qui garde tout en mémoire: les images source ainsi que les images de sortie. Ceci est connu comme traitement "en mode immédiat". Lorsque vous appelez une méthode pour redimensionner l'image, cela arrive immédiatement. Par conséquent, vous gardez toujours les images en mémoire.

Avec JAI, vous bénéficiez de deux avantages.

  1. Traitement en mode différé.
  2. Calcul de carreaux.

Le mode différé signifie que les images de sortie ne sont pas calculées correctement lorsque vous appelez des méthodes sur les images. Au lieu de cela, un appel à redimensionner une image crée un petit objet "opérateur" qui peut effectuer le redimensionnement plus tard. Cela vous permet de construire des chaînes, des arbres ou des pipelines d'opérations. Ainsi, votre travail permettrait de construire un arbre d'opérations comme "recadrer, redimensionner, composite" pour chaque image de stock.La bonne partie est que les opérations ne sont que des objets de commande, donc vous ne consommez pas toute la mémoire pendant que vous construisez vos commandes.

Cette API est basée sur le pull. Il reporte le calcul jusqu'à ce que l'action de sortie tire pixels des opérateurs. Cela permet de gagner rapidement du temps et de la mémoire en évitant les opérations inutiles sur les pixels. Par exemple, supposons que vous ayez besoin d'une image de sortie de 2048 x 2048 pixels, mise à l'échelle à partir d'une image 512x512 issue d'une image source de 1600x512 pixels. Évidemment, cela n'a aucun sens d'agrandir l'image source entière de 1600x512, juste pour jeter les 2/3 des pixels. Au lieu de cela, l'opérateur de mise à l'échelle aura une "région d'intérêt" (ROI) basée sur ses dimensions de sortie. L'opérateur de mise à l'échelle projette le ROI sur l'image source et calcule uniquement ces pixels.

Les commandes doivent éventuellement être évaluées. Cela se produit dans quelques situations, principalement liées à la sortie de l'image finale. Donc, demander une BufferedImage pour afficher la sortie sur l'écran forcera toutes les commandes à évaluer. De même, l'écriture de l'image de sortie sur le disque force l'évaluation.

Dans certains cas, vous pouvez conserver le second avantage de JAI, qui est le rendu basé sur les vignettes. Tandis que BufferedImage effectue tout son travail immédiatement, sur tous les pixels, le rendu des vignettes ne fonctionne que sur des sections rectangulaires de l'image à la fois. En utilisant l'exemple d'avant, l'image de sortie 2048x2048 sera divisée en mosaïques. Supposons que ceux-ci sont 256x256, alors l'image entière est divisée en 64 tuiles. Les objets opérateurs JAI savent comment travailler une tuile sur une tuile. Ainsi, la mise à l'échelle de la section 512x512 de l'image source se produit réellement 64 fois sur des pixels source 64x64 à la fois.

Calculer une tuile à la fois signifie faire une boucle sur les tuiles, ce qui semblerait prendre plus de temps. Cependant, deux choses fonctionnent en votre faveur lors du calcul de tuiles. Tout d'abord, les tuiles peuvent être évaluées simultanément sur plusieurs threads. Deuxièmement, l'utilisation de la mémoire transitoire est beaucoup, beaucoup plus faible que le calcul du mode immédiat.

Tout ce qui est une longue explication pourquoi vous voulez utiliser JAI pour ce type de traitement d'image.


Quelques notes et mises en garde:

  1. Vous pouvez vaincre le rendu à base de tuiles sans le savoir. Partout où vous avez un BufferedImage dans le flux de travail, il ne peut pas agir comme une source de tuile ou un puits.
  2. Si vous effectuez un rendu sur disque à l'aide des opérateurs d'E/S d'image JAI ou JAI pour JPEG, vous êtes en bon état. Si vous essayez d'utiliser les classes d'image intégrées du JDK, vous aurez besoin de toute la mémoire. (Fondamentalement, évitez de mélanger les deux types de manipulation d'image.Le mode immédiat et le mode différé ne se mélangent pas bien.)
  3. Tous les trucs fantaisie avec les ROI, les tuiles et le mode différé sont transparents pour le programme. Vous venez de faire un appel d'API sur la classe JAI. Vous ne traitez avec la machine que si vous avez besoin de plus de contrôle sur des choses comme la taille des tuiles, la mise en cache et la concurrence.
0

Chaque fois que vous append 'êtes-vous peut-être en train de créer implicitement un nouvel objet avec un plus pixel pour remplacer l'ancien (c.-à-un parallèle au problème classique de annexant à plusieurs reprises à une chaîne au lieu d'utiliser un StringBuilder)?

Si vous publiez la partie de votre code qui contient le stockage et l'ajout, quelqu'un vous aidera probablement à trouver un moyen efficace de le recoder.

2

Voici une suggestion qui pourrait être utile; Essayez de séparer les deux tâches principales en programmes individuels.Votre première tâche est de décider quelles images vont où, et qui peut être un simple mappage de coordonnées aux noms de fichiers, qui peuvent être représentés sous forme de lignes de texte:

0,0,image123.jpg 
0,1,image542.jpg 
..... 

Après cette tâche est fait (et il semble que vous avoir bien géré), alors vous pouvez avoir un programme séparé gérer la compilation.

Cette compilation peut être effectuée en ajoutant une image, mais vous ne voulez probablement pas vous tromper avec les formats de fichiers. Il est préférable de laisser votre environnement de programmation le faire en utilisant un objet Image Java quelconque. Le plus grand que vous pouvez insérer dans la mémoire pixelwise sera de 2 Go conduisant à sqrt (2x10^9) hauteur maximale et la largeur. A partir de ce nombre et en divisant par le nombre d'images que vous avez pour la hauteur et la largeur, vous obtiendrez les pixels globaux par sous-image autorisés., Et vous pouvez les peindre dans les endroits appropriés.

Questions connexes