9
votes

Jpegs rotatifs dans .NET avec une perte minimale de qualité

Je tente de prendre en charge des images JPEG en rotation de ASP.NET MVC (par incréments de 90 degrés). Je tente d'utiliser System.Drawing code> (GDI +), mais je rencontre des problèmes.

J'ai essayé d'utiliser image.rotateflip code> capable de faire pivoter l'image mais provoque une perte de qualité. Même avec une qualité de codeur de 100, il existe toujours des artefacts visibles sur l'image en rotation qui n'étaient pas sur l'image d'origine et ne se présentent pas quand je le fais pivoter à l'aide d'autres programmes (GIMP, etc.). P>

if (sourceFormat.Guid == ImageFormat.Jpeg.Guid) {


2 commentaires

Votre code fonctionne pour moi .. Êtes-vous sûr qu'un imagecodecinfo est renvoyé pour vos données?


Merci @pb, je recevais un imagecodecinfo mais pas un encoderparams car mon code actuel avait un chèque supplémentaire avant de la définir qui avait un bogue.


3 Réponses :


4
votes

Merci d'avoir confirmé que mon code posté fonctionnait. Cela m'a aidé à isoler mon problème. Je me sens stupide maintenant. Mon code actuel a eu une vérification du format d'image avant de définir encoderparams - mais il a eu un bug: xxx

j'ai découvert que le conditionnel ci-dessus était toujours faux si coderparams n'était pas défini. Le correctif était simple: xxx


0 commentaires

2
votes

Avec n'importe quelle méthode qui décompresse une image comprimée à perte de pertes, la rotation et la perte de la permission de la compresse à nouveau (même avec le même paramètre), vous obtiendrez une perte de qualité. Pendant la décompression pertinente, vous ne recevez qu'une approximation de l'image comprimée par la perte (rappelez-vous que la compression est définie en termes de PSNR, une autre implémentation peut donc décompresser la même image légèrement différemment). Même chose pour la ré-compression, à moins que vous ne preniez exactement le même compresseur à perte et les mêmes paramètres, il est impossible de recompresser l'image menant aux mêmes valeurs DCT quantifiées.

Le format JPEG comprime les informations de couleur dans des carrés de 2x2 pixels en obtenant une couleur moyenne pour représenter les quatre pixels, de sorte que votre largeur d'image et votre hauteur sont divisibles par deux, vous perdrez moins de qualité comme la plupart des informations supprimées dans la compression. est des informations interpolées dans la décompression.

De même, les informations de luminosité sont comprimées dans des carrés de 8x8 pixels, donc si votre largeur et votre hauteur sont divisables de huit, la grille s'alignera après la rotation de l'image et vous perdrez moins d'informations réelles.

Pour faire une rotation sans perte, vous devez utiliser une méthode complètement différente, lire le fichier JPEG et réorganiser et faire pivoter chaque carré d'informations (valeurs DCT quantifiées) de sorte qu'il forme l'image en rotation sans décompresser et le recompréger (l'encodage d'entropie est un mécanisme sans perte qui peut être trébuché en toute sécurité).


3 commentaires

OP demander spécifiquement une transformation sans perte et des incréments de 90 ° avec l'application à JPEG. Il est donc assez clair que faire pour préserver la qualité de l'entrée exactement.


@Malat: Oui, je suis d'accord que c'est assez clair. C'est ce que j'ai écrit dans cette réponse. Y a-t-il quelque chose que vous êtes en désaccord avec?


Voici comment je l'aurais écrit, je pense que cela n'est toujours pas clair lorsque la rotation peut être obtenue lors de l'utilisation du sous-échantillonnage de la chroma 4: 2: 0, un utilisateur peut être confondu par votre référence à une référence de bloc 8x8.



1
votes

Voici mon adaptation de la réponse de Mike Henry modifiée à mes besoins pour ceux qui peuvent le trouver utiles.

public MemoryStream RotateImage(Stream stream, RotateFlipType rotationFlipType)
{
    try
    {
        if (stream != null)
        {
            using (Image image = Image.FromStream(stream))
            {
                ImageFormat sourceFormat = image.RawFormat;
                image.RotateFlip(rotationFlipType);
                EncoderParameters encoderParams = null;
                try
                {
                    if (sourceFormat.Guid == ImageFormat.Jpeg.Guid)
                    {
                        encoderParams = new EncoderParameters(1);
                        encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
                    }
                    var ms = new MemoryStream();
                    image.Save(ms,
                        ImageCodecInfo.GetImageEncoders().FirstOrDefault(e => e.FormatID == sourceFormat.Guid),
                        encoderParams);
                    ms.Position = 0;
                    return ms;
                }
                finally
                {
                    if (encoderParams != null)
                        encoderParams.Dispose();
                }
            }

        }
    }
    finally
    {
        if (stream != null)
        {
            stream.Dispose();
        }
    }
    return null;
}


0 commentaires