IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Affichage rapide d'images


Sujet :

C#

  1. #1
    Membre régulier
    Homme Profil pro
    autre
    Inscrit en
    Janvier 2015
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Janvier 2015
    Messages : 142
    Points : 108
    Points
    108
    Par défaut Affichage rapide d'images
    Bonjour,

    Je souhaite afficher rapidement un grand nombre d'images présentes dans un répertoire.
    Les images sont assez grosses. En moyenne 1200x800 px et 1 Mo.
    Il y en a 4000 dans le répertoire.

    C'est la troisième fois que je reprends à zéro ce projet, tant le problème du temps d'affichage des images dans une listview est rédhibitoire.
    Ça va jusqu'à plusieurs minutes d'attente pour tout charger (et je les redimensionne avant de charger l'ImageList).
    À chaque trie ou filtre ça recommence.

    Existe-t-il un package qui prendrait en charge l'affichage rapide d'un grand nombre d'images ?
    Je n'ai pas à les transformer, à les éditer etc... C'est une gestion de collection de tableaux de peintres.

    Mes recherches ne m'ont rien renvoyé de concluant ou d'adapté.
    J'ai uniquement besoin de les afficher rapidement en miniatures (environ 200x200).

    Un logiciel comme FastStone fait très bien cela, par exemple.

    Merci,

  2. #2
    Membre expérimenté
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    1 126
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 1 126
    Points : 1 636
    Points
    1 636
    Par défaut
    une demande à mon ami google sur c# .net miniature et hop, des pistes intéressantes (non testés):
    en winforms: https://learn.microsoft.com/fr-fr/do...orkdesktop-4.8
    en wpf: https://learn.microsoft.com/fr-fr/do...orkdesktop-4.8
    (comme on ne sait pas ce que tu as essayé)
    En tout cas, il faut éviter de refaire l'opération à chaque fois, c'est sûr.

  3. #3
    Membre régulier
    Homme Profil pro
    autre
    Inscrit en
    Janvier 2015
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Janvier 2015
    Messages : 142
    Points : 108
    Points
    108
    Par défaut
    Bonjour umfred,

    Merci, je vais voir ce que cela donne avec tes liens.

    Cependant j'ai déjà une méthode pour créer des miniatures

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
            public System.Drawing.Image Padding_Reduce_Image(System.Drawing.Image image, Color backColor, int height, int width)
            {
     
                //get the maximum size of the image dimensions
                int maxSize = Math.Max(image.Height, image.Width);
                Size squareSize = new Size(maxSize, maxSize);
     
                //create a new square image
                Bitmap squareImage = new Bitmap(squareSize.Width, squareSize.Height);
     
                using (Graphics graphics = Graphics.FromImage(squareImage))
                {
                    //fill the new square with a color
                    graphics.FillRectangle(new SolidBrush(backColor), 0, 0, squareSize.Width, squareSize.Height);
     
                    //put the original image on top of the new square
                    graphics.DrawImage(image, (squareSize.Width / 2) - (image.Width / 2), (squareSize.Height / 2) - (image.Height / 2), image.Width, image.Height);
                }
     
                try
                {
                    //Image image = Image.FromStream(resourceImage);
                    Image thumb = squareImage.GetThumbnailImage(width, height, () => false, IntPtr.Zero);
                    return thumb;
     
     
                }
                catch (Exception)
                {
                    return null;
                }
                //return the image
            }
    Je n'arrive pas à comprendre d'où vient le fait que cela prenne autant de temps dans mon code.
    Si c'est lié à un problème de structure de mon code, ou si c'est lié au temps intrinsèque des opérations successives que je lui fait réaliser.
    Accéder à chaque fichier image du répertoire > Convertir l'image en miniature > Charger ImageList > Afficher dans ListView

    Je me demandais si n'existait pas une solution "clé en main" Ou une approche plus économe genre Lazy Load

    Merci

  4. #4
    Membre éprouvé Avatar de WDKyle
    Homme Profil pro
    Analyste-Programmeur
    Inscrit en
    Septembre 2008
    Messages
    1 200
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-Programmeur

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 200
    Points : 962
    Points
    962
    Par défaut
    Bonjour,

    Utilisez-vous du multithreading pour effectuer ses taches de façon parallélisées ?

  5. #5
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Tu n'affiches pas les 4000 images en même temps, donc pourquoi les charger toutes
    Il suffit de les charger au fur et à mesure du scroll (et en multithreadé surement)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  6. #6
    Membre régulier
    Homme Profil pro
    autre
    Inscrit en
    Janvier 2015
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Janvier 2015
    Messages : 142
    Points : 108
    Points
    108
    Par défaut
    Bonjour WDKyle,

    Oui j'utilise le multithread.

    J'ai aussi imaginé créer un sous-répertoire pour stocker les miniatures.
    Ce qui m'oblige à vérifier à chaque étapes la cohérence entre les images grand format, les miniatures, et la base de données.
    Opérations très contraignantes qui multiplient les sources et occasions d'erreurs.

    J'avoue que je reviens sur ce projet après des mois de pause.
    Je suis prêt à le reprendre entièrement après avoir bien identifié que mon problème principal est la lenteur d'affichage des images présentes dans le répertoire.

    D'où ma demande pour savoir s'il n'existait pas un package qui ferait déjà cela mieux que je pourrais le faire.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
     
            private void BackWorkerImage_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                BackgroundWorker worker = sender as BackgroundWorker;
     
                List<object> genericlist = e.Argument as List<object>;
                string monPath = (string)genericlist[0];
                int Value1 = (int)genericlist[1]; // Nb d'images à charger
     
                if (ProgBarImg.InvokeRequired) //ProgBarImg.Maximum = Value1 + 10;
                    ProgBarImg.Invoke((MethodInvoker)delegate ()
                    {
                        ProgBar_Max(Value1 + 10);
                    });
                else ProgBarImg.Maximum = Value1 + 10;
     
     
                if (ChargerImagelist(monPath, worker, e) == false) // Pb filestream rencontré donc on essaie une deuxième fois
                {
                    if (ChargerImagelist(monPath, worker, e) == false)
                    {
                        string Message = "Un fichier est temporairement inaccessible.\n Veuillez relancer l'affichage des images ultérieurement en cliquant sur l'icône Rafraîchir";
                        string Caption = "Affichage des images du disque";
                        var result = FlexibleMessageBox.Show(Message, Caption, MessageBoxButtons.OK,
                                                             MessageBoxIcon.Information,
                                                             MessageBoxDefaultButton.Button1);
                        OeuvresImagesList.Images.Clear();
                        return;
                    }
                }
            }
            private bool ChargerImagelist(string directory, BackgroundWorker worker, System.ComponentModel.DoWorkEventArgs e)
            {
                Debug.WriteLine("ChargerImagelist");
                string NomFic;
                OeuvresImagesList.Images.Clear();
     
                try
                {
                    OeuvresListView.LargeImageList = OeuvresImagesList;
                    OeuvresListView.LargeImageList.ImageSize = new Size(150, 150);
                    OeuvresListView.LargeImageList.ColorDepth = ColorDepth.Depth16Bit;
                    OeuvresListView.View = View.LargeIcon;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
     
                if (Directory.Exists(directory + "\\Thumbs")) 
                {
                    // Si Thumbs existe on compare leur contenu
     
                    System.IO.DirectoryInfo dir1 = new System.IO.DirectoryInfo(directory);
                    System.IO.DirectoryInfo dir2 = new System.IO.DirectoryInfo(directory + "\\Thumbs");
     
                    // Take a snapshot of the file system.  
                    IEnumerable<System.IO.FileInfo> list1 = dir1.GetFiles("*.jpg", System.IO.SearchOption.TopDirectoryOnly);
                    IEnumerable<System.IO.FileInfo> list2 = dir2.GetFiles("*.jpg", System.IO.SearchOption.TopDirectoryOnly);
     
                    FileCompare myFileCompare = new FileCompare();
     
                    bool areIdentical = list1.SequenceEqual(list2, myFileCompare);
     
                    if (areIdentical == true) // Répertoires IDENTIQUES --> que des thumbnails 150 x 150 à charger
                    {
                        Debug.WriteLine("------------- Les répertoires sont identiques");
                        Remplis_ImageList(The_Directory + "\\Thumbs");
                    }
                    else // Répertoires DIFFERENTS
                    {
                        Debug.WriteLine("------------- Les répertoires sont différents");
     
                        Color CouleurFond = new Color();
                        CouleurFond = System.Drawing.Color.Transparent;
     
                        var queryCommonFiles = list1.Intersect(list2, myFileCompare);
     
                        if (queryCommonFiles.Any() == false)
                        {
                            Debug.WriteLine("------------- Il n'y a pas de fichier commun aux deux répertoires.");
     
                            Directory.Delete(directory + "\\Thumbs");// ---> Supp tous fichiers de Thumbs et le reconstruire
                            Create_THUMBS();
                        }
                        else // --> Seulement certains fichiers sont à traiter 
                        {
                            // Différences entre les deux répertoires  
                            //---------------------
                            var queryList1 = (from file in list1
                                              select file).Except(list2, myFileCompare);
     
                            Debug.WriteLine("------------- Les fichiers suivants sont dans The_Directory mais pas dans 'Thumbs' : ");
                            foreach (var v in queryList1)
                            {
                                Debug.WriteLine(" -- Création : " + v.Name);
     
                                FileStream fichier = new FileStream(v.FullName, FileMode.Open);
     
                                NomFic = v.Name;
                                string TargetFile = directory + "\\Thumbs\\" + NomFic;
     
                                Bitmap Image1 = new Bitmap(fichier);
                                Image newImage = Padding_Reduce_Image(Image1, CouleurFond, 150, 150);
                                newImage.Save(TargetFile, System.Drawing.Imaging.ImageFormat.Png);
     
                                fichier.Close();
                                fichier.Dispose();
                            }
                            //---------------------
                            var queryList2 = (from file in list2
                                              select file).Except(list1, myFileCompare);
     
                            Debug.WriteLine("------------- Les fichiers suivants sont dans 'Thumbs' mais absents de The_Directory : ");
                            foreach (var v in queryList2)
                            {
                                Debug.WriteLine(" -- Suppression : " + v.Name);
                                File.Delete(v.FullName);
                            }
     
                            Remplis_ImageList(The_Directory + "\\Thumbs"); // On rempli ensuite. !! le répertoire est trié 
                        }
                    }
                }
                else // Si Thumbs n'existe pas, on le crée avec des Thumnails de 150 x 150 px
                {
                    Create_THUMBS();
                }
     
                if (OeuvresListView.InvokeRequired)//OeuvresListView.Show();
                    OeuvresListView.Invoke((MethodInvoker)delegate ()
                    {
                        OeuvresListView_Show();
                    });
                else OeuvresListView.Show();
     
                Debug.WriteLine("Nb Images : " + OeuvresImagesList.Images.Count);
                return true;
            }

  7. #7
    Membre régulier
    Homme Profil pro
    autre
    Inscrit en
    Janvier 2015
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Janvier 2015
    Messages : 142
    Points : 108
    Points
    108
    Par défaut
    Citation Envoyé par Pol63 Voir le message
    Tu n'affiches pas les 4000 images en même temps, donc pourquoi les charger toutes
    Il suffit de les charger au fur et à mesure du scroll (et en multithreadé surement)
    Bonjour Pol63,

    Oui, c'est aussi une idée. Un lazy Load. C'est évidemment la bonne façon de faire.
    Je n'ai pas creusé cette voie. J'imagine que l'on peut récupérer la liste des items visibles dans une ListView plus ou moins 10.
    Charger l'ImageList avec une miniature temporaire et remplacer les images au fur et à mesure de ce qui est demandé.
    Je dois tester ça.

  8. #8
    Membre éprouvé Avatar de WDKyle
    Homme Profil pro
    Analyste-Programmeur
    Inscrit en
    Septembre 2008
    Messages
    1 200
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-Programmeur

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 200
    Points : 962
    Points
    962
    Par défaut
    Bonjour,

    Je vois que vous passez un `BackgroundWorker` à la méthode `ChargerImagelist` mais vous ne l'utilisez pas.

    De plus, je vois que vous affichez une jauge pour montrer l'avancement du traitement mais comme tout est synchrone c'est normal votre traitement soit long.

    Petite info sinon, ne mettez pas 4000 fichiers dans un répertoire mais créez des sous-repertoires de 500 fichiers max. Cela rendra votre traitement plus rapide, Windows n'aime pas les répertoires trop rempli.

  9. #9
    Membre régulier
    Homme Profil pro
    autre
    Inscrit en
    Janvier 2015
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Janvier 2015
    Messages : 142
    Points : 108
    Points
    108
    Par défaut
    Citation Envoyé par WDKyle Voir le message
    Bonjour,

    Je vois que vous passez un `BackgroundWorker` à la méthode `ChargerImagelist` mais vous ne l'utilisez pas.

    De plus, je vois que vous affichez une jauge pour montrer l'avancement du traitement mais comme tout est synchrone c'est normal votre traitement soit long.

    Petite info sinon, ne mettez pas 4000 fichiers dans un répertoire mais créez des sous-repertoires de 500 fichiers max. Cela rendra votre traitement plus rapide, Windows n'aime pas les répertoires trop rempli.
    D'accord. C'est vrai que le 'BackgroundWorker' passé en paramètre n'est pas utilisé.
    'ChargerImagelist' est appelée dans 'BackWorkerImage_DoWork' donc je pense que cela s'exécute bien en parallèle dans un autre Thread.
    Ok pour la progressBar, je vais supprimer ça. Invoke/Delegate doit ralentir l'exécution.
    Merci

  10. #10
    Expert éminent sénior

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 760
    Points : 10 541
    Points
    10 541
    Billets dans le blog
    21
    Par défaut
    Bonjour,

    Vu les bribes d'API, je suppose que tu utilises du Windows Form. Le control ListView n'est pas vraiment adapté pour charger une grosse quantité de données (tout est en mémoire, donc tout doit être créé avant).

    Il n'y a pas de composant natif permettant de gérer une large collection de données. Aussi, voici comment je procèderai :
    - je créerai un composant constitué d'un ListView et d'un VScrollBar
    - dans ce composant, le VScrollBar sera géré manuellement pour refléter la position en fonction du nombre d'éléments à afficher
    - dans ce composant toujours, la ListView n'affichera qu'un nombre réduit d'item (par ex. 15 ou 20)
    - bien évidement, il faudra synchroniser également les deux composants (d'où l'idée de faire un composant qui encapsule tout ça)
    - ensuite, il faudra gérer la création des miniatures, et pour ça, je verrais bien un système de type producteur / consommateur
    - un producteur produit la liste des miniatures à afficher (les items contenus dans la listview, mais aussi pourquoi pas un delta de +/- 10 éléments avant/après par exemple afin de pouvoir avoir un scrolling rapide)
    - un ou des consommateurs produisent les miniatures soit directement via le cache, soit si nécessaire depuis l'image)
    - l'avantage du producteur / consommateur est que cela permet de facilement paralléliser les tâches (aussi bien du point de vue producteur que consommateur, même si la parallélisation du point de vue producteur ne me parait pas utile ici).

    Cela demande un peu de boulot, mais les ListView ne sont pas vraiment pas fait pour de nombreux éléments, et encore moins quand il y a des images...
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  11. #11
    Membre régulier
    Homme Profil pro
    autre
    Inscrit en
    Janvier 2015
    Messages
    142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Janvier 2015
    Messages : 142
    Points : 108
    Points
    108
    Par défaut
    Bonjour François DORIN,
    Merci beaucoup pour ces explications et ces pistes.
    Je vais me pencher sur ce système Producteur/Consommateur que je ne connais pas du tout.

Discussions similaires

  1. Capture d'images (rapide) et affichage (rapide)
    Par dtcSearch dans le forum Windows Presentation Foundation
    Réponses: 1
    Dernier message: 14/04/2011, 14h09
  2. Pbs d'affichage d'une image sur un panel
    Par ysr1 dans le forum C++Builder
    Réponses: 2
    Dernier message: 23/09/2004, 09h55
  3. [FLASH MX] Qualité d'affichage d'une image
    Par n_tony dans le forum Flash
    Réponses: 3
    Dernier message: 16/08/2004, 09h44
  4. [DirectDraw7] Affichage de plusieurs image bmp
    Par SteelBox dans le forum DirectX
    Réponses: 3
    Dernier message: 24/04/2004, 19h00
  5. Affichage d'une image sous linux
    Par Braim dans le forum x86 32-bits / 64-bits
    Réponses: 5
    Dernier message: 25/03/2003, 10h41

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo