web-dev-qa-db-fra.com

Redimensionner l'image pour tenir dans la boîte de sélection

Un problème facile, mais pour une raison quelconque, je ne peux tout simplement pas comprendre cela aujourd'hui.

J'ai besoin de redimensionner une image à la taille maximale possible qui s'intégrera dans une zone de sélection tout en maintenant le rapport de format.

De manière fondamentale, je cherche le code à remplir cette fonction:

void CalcNewDimensions(ref int w, ref int h, int MaxWidth, int MaxHeight);

Où W & H sont la hauteur et la largeur d'origine (IN) et la nouvelle hauteur et la nouvelle largeur (sortie) et maxwidth et maxheight définissent la boîte de sélection que l'image doit s'intégrer.

39
Eric Petroelje

Trouvez qui est plus petit: MaxWidth / w ou MaxHeight / h Puis multiplier w et h par ce numéro

Explication :

Vous devez trouver le facteur d'échelle qui rend l'image en forme.

Pour trouver le facteur d'échelle, s, pour la largeur, alors s doit être tel que: s * w = MaxWidth. Par conséquent, le facteur d'échelle est MaxWidth / w.

De même pour la taille.

Celui qui nécessite la plus grande échelle (plus petit s) est le facteur que vous devez mettre à l'échelle de l'image entière.

86
Shawn J. Goff

Basé sur la suggestion d'Eric, je ferais quelque chose comme ça:

private static Size ExpandToBound(Size image, Size boundingBox)
{       
    double widthScale = 0, heightScale = 0;
    if (image.Width != 0)
        widthScale = (double)boundingBox.Width / (double)image.Width;
    if (image.Height != 0)
        heightScale = (double)boundingBox.Height / (double)image.Height;                

    double scale = Math.Min(widthScale, heightScale);

    Size result = new Size((int)(image.Width * scale), 
                        (int)(image.Height * scale));
    return result;
}

Je serais peut-être allé un peu à la mer sur les cases, mais j'essayais simplement de préserver la précision dans les calculs.

27
Matt Warren

J'ai essayé le code de M. Warren, mais cela n'a pas produit de résultats fiables.

Par exemple,

ExpandToBound(new Size(640,480), new Size(66, 999)).Dump();
// {Width=66, Height=49}

ExpandToBound(new Size(640,480), new Size(999,50)).Dump();
// {Width=66, Height=50}

Vous pouvez voir, hauteur = 49 et hauteur = 50 dans une autre.

Voici la mienne (version basée sur le code de M. Warren) sans la divergence et un léger refacteur:

// Passing null for either maxWidth or maxHeight maintains aspect ratio while
//        the other non-null parameter is guaranteed to be constrained to
//        its maximum value.
//
//  Example: maxHeight = 50, maxWidth = null
//    Constrain the height to a maximum value of 50, respecting the aspect
//    ratio, to any width.
//
//  Example: maxHeight = 100, maxWidth = 90
//    Constrain the height to a maximum of 100 and width to a maximum of 90
//    whichever comes first.
//
private static Size ScaleSize( Size from, int? maxWidth, int? maxHeight )
{
   if ( !maxWidth.HasValue && !maxHeight.HasValue ) throw new ArgumentException( "At least one scale factor (toWidth or toHeight) must not be null." );
   if ( from.Height == 0 || from.Width == 0 ) throw new ArgumentException( "Cannot scale size from zero." );

   double? widthScale = null;
   double? heightScale = null;

   if ( maxWidth.HasValue )
   {
       widthScale = maxWidth.Value / (double)from.Width;
   }
   if ( maxHeight.HasValue )
   {
       heightScale = maxHeight.Value / (double)from.Height;
   }

   double scale = Math.Min( (double)(widthScale ?? heightScale),
                            (double)(heightScale ?? widthScale) );

   return new Size( (int)Math.Floor( from.Width * scale ), (int)Math.Ceiling( from.Height * scale ) );
}
7
Brian Chavez

Pour effectuer un remplissage d'aspect au lieu d'un aspect, utilisez plutôt le ratio plus grand. C'est-à-dire, changez le code de Matt de MATH.MIN à Math.Max.

(Un aspect remplissage ne laisse aucune de la boîte de sélection vide mais peut mettre une partie de l'image en dehors des limites, tandis qu'un aspect ajustement ne laisse aucune de l'image en dehors des limites, mais peut laisser une partie de la boîte de sélection vide.)

7
vocaro

Le code suivant produit des résultats plus précis:

    public static Size CalculateResizeToFit(Size imageSize, Size boxSize)
    {
        // TODO: Check for arguments (for null and <=0)
        var widthScale = boxSize.Width / (double)imageSize.Width;
        var heightScale = boxSize.Height / (double)imageSize.Height;
        var scale = Math.Min(widthScale, heightScale);
        return new Size(
            (int)Math.Round((imageSize.Width * scale)),
            (int)Math.Round((imageSize.Height * scale))
            );
    }
5
M. Mennan Kara

Über simple. :) La question est de trouver un facteur par lequel vous devez multiplier la largeur et la hauteur. La solution consiste à essayer d'utiliser un et s'il ne convient pas, utilisez l'autre. Donc...

private float ScaleFactor(Rectangle outer, Rectangle inner)
{
    float factor = (float)outer.Height / (float)inner.Height;
    if ((float)inner.Width * factor > outer.Width) // Switch!
        factor = (float)outer.Width / (float)inner.Width;
    return factor;
}

Pour adapter l'image (PCTRect) à la fenêtre (wndrect) appelle comme celle-ci

float factor=ScaleFactor(wndRect, pctRect); // Outer, inner
RectangleF resultRect=new RectangleF(0,0,pctRect.Width*factor,pctRect.Height*Factor)
3
Tomaž Štih

Code Python, mais peut-être que cela vous dirigera dans la bonne direction:

def fit_within_box(box_width, box_height, width, height):
    """
    Returns a Tuple (new_width, new_height) which has the property
    that it fits within box_width and box_height and has (close to)
    the same aspect ratio as the original size
    """
    new_width, new_height = width, height
    aspect_ratio = float(width) / float(height)

    if new_width > box_width:
        new_width = box_width
        new_height = int(new_width / aspect_ratio)

    if new_height > box_height:
        new_height = box_height
        new_width = int(new_height * aspect_ratio)

    return (new_width, new_height)
2
Jason Creighton

Basé sur les réponses précédentes, voici une fonction JavaScript:

/**
* fitInBox
* Constrains a box (width x height) to fit in a containing box (maxWidth x maxHeight), preserving the aspect ratio
* @param width      width of the box to be resized
* @param height     height of the box to be resized
* @param maxWidth   width of the containing box
* @param maxHeight  height of the containing box
* @param expandable (Bool) if output size is bigger than input size, output is left unchanged (false) or expanded (true)
* @return           {width, height} of the resized box
*/
function fitInBox(width, height, maxWidth, maxHeight, expandable) {
    "use strict";

    var aspect = width / height,
        initWidth = width,
        initHeight = height;

    if (width > maxWidth || height < maxHeight) {
        width = maxWidth;
        height = Math.floor(width / aspect);
    }

    if (height > maxHeight || width < maxWidth) {
        height = maxHeight;
        width = Math.floor(height * aspect);
    }

    if (!!expandable === false && (width >= initWidth || height >= initHeight)) {
        width = initWidth;
        height = initHeight;
    }

    return {
        width: width,
        height: height
    };
}

Je voudrais un problème similaire et j'ai trouvé cela très utile: article . Comme j'ai compris correctement, vous devez redimensionner l'image?

0
bezieur