Tengo un bloque de imágenes de los productos que hemos recibido de un cliente. Cada producto de la imagen es una foto de algo y que fue tomada con un fondo blanco. Me gustaría recortar todas las partes circundantes de la imagen, pero dejar sólo el producto en el medio. Es esto posible?

Como un ejemplo: [http://www.5dnet.de/media/catalog/product/d/r/dress_shoes_5.jpg][1]

No quiero que todos los píxeles blancos eliminado, sin embargo yo quiero que la imagen recortada de modo que la fila más alta de píxeles que contiene una no-píxel blanco, más a la izquierda de la vertical de la fila de píxeles que contiene una no-píxel blanco, horizontal inferior de la fila de píxeles que contiene una no-píxel blanco, etc.

Código en C# o VB.net sería apreciada.

InformationsquelleAutor Kyle Ballard | 2008-10-29

8 Comentarios

  1. 7

    He escrito código para hacer esto por mí mismo – que no es demasiado difícil de obtener los conceptos básicos de ir.

    Esencialmente, usted necesita para escanear pixel filas/columnas para comprobar la no-píxeles en blanco y aislar los límites de la imagen del producto, a continuación, crear un nuevo mapa de bits con sólo esa región.

    Tenga en cuenta que mientras que el Bitmap.GetPixel() método funciona, es relativamente lento. Si el tiempo de procesamiento es importante, usted necesitará utilizar Bitmap.LockBits() para bloquear el mapa de bits en la memoria y, a continuación, algunos simple puntero de su uso dentro de una unsafe { } bloque para tener acceso a los píxeles directamente.

    Este artículo en CodeProject da algunos detalles más que usted probablemente encontrará útil.

    • Esto toma demasiado tiempo para procesar un 1000×1000 Imagen? Por favor, consejos.
    • Depende de tu definición de «demasiado tiempo» – que depende de su contexto. Te sugiero que escribir el código que utiliza Bitmap.GetPixel() y, a continuación, el benchmarking, el resultado para ver. También tenga en cuenta que un algoritmo inteligente, es más importante que la micro-optimización de píxel individual lee.
  2. 39

    Me di cuenta de que tenía que ajustar Dmitri respuesta para asegurarse de que funciona con imágenes que en realidad no necesita de cultivo (ya sea horizontal, vertical o ambos)…

        public static Bitmap Crop(Bitmap bmp)
    {
    int w = bmp.Width;
    int h = bmp.Height;
    Func<int, bool> allWhiteRow = row =>
    {
    for (int i = 0; i < w; ++i)
    if (bmp.GetPixel(i, row).R != 255)
    return false;
    return true;
    };
    Func<int, bool> allWhiteColumn = col =>
    {
    for (int i = 0; i < h; ++i)
    if (bmp.GetPixel(col, i).R != 255)
    return false;
    return true;
    };
    int topmost = 0;
    for (int row = 0; row < h; ++row)
    {
    if (allWhiteRow(row))
    topmost = row;
    else break;
    }
    int bottommost = 0;
    for (int row = h - 1; row >= 0; --row)
    {
    if (allWhiteRow(row))
    bottommost = row;
    else break;
    }
    int leftmost = 0, rightmost = 0;
    for (int col = 0; col < w; ++col)
    {
    if (allWhiteColumn(col))
    leftmost = col;
    else
    break;
    }
    for (int col = w - 1; col >= 0; --col)
    {
    if (allWhiteColumn(col))
    rightmost = col;
    else
    break;
    }
    if (rightmost == 0) rightmost = w; //As reached left
    if (bottommost == 0) bottommost = h; //As reached top.
    int croppedWidth = rightmost - leftmost;
    int croppedHeight = bottommost - topmost;
    if (croppedWidth == 0) //No border on left or right
    {
    leftmost = 0;
    croppedWidth = w;
    }
    if (croppedHeight == 0) //No border on top or bottom
    {
    topmost = 0;
    croppedHeight = h;
    }
    try
    {
    var target = new Bitmap(croppedWidth, croppedHeight);
    using (Graphics g = Graphics.FromImage(target))
    {
    g.DrawImage(bmp,
    new RectangleF(0, 0, croppedWidth, croppedHeight),
    new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
    GraphicsUnit.Pixel);
    }
    return target;
    }
    catch (Exception ex)
    {
    throw new Exception(
    string.Format("Values are topmost={0} btm={1} left={2} right={3} croppedWidth={4} croppedHeight={5}", topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight),
    ex);
    }
    }
    • Buen trabajo amigo, y gracias por compartir!
    • Los archivos PNG con el ‘transparente en los espacios en blanco’ he añadido la comprobación de bmp.GetPixel(i, fila).A != 0 y bmp.GetPixel(col, yo).A != 0 (nivel alfa de cero). Ahora mi Png están funcionando muy bien. Gracias por el código! Nota: puedo ejecutar bmp.GetPixel() una vez, a continuación, analizar R y propiedades para evitar la doble análisis.
    • Voy a agregar un caso extra que necesitan ser tratados: tengo una pequeña imagen y voy a utilizar un FillRectangle(Brushes.White,...) -> entonces mi imagen no sólo 255 así que mejor debería corregir. Los valores de blanco: W:0 H:0 a:255 b:254 g:254 r:254 W:1 H:0 a:255 b:254 g:254 r:254 W:2 H:0 a:255 b:254 g:254 r:254 W:3 H:0 a:255 b:254 g:254 r:254 W:4 H:0 a:255 b:254 g:254 r:254 W:5 H:0 a:255 b:254 g:254 r:254 W:6 H:0 a:255 b:254 g:254 r:254 W:7 H:0 a:255 b:254 g:254 r:254 W:8 H:0 a:255 b:254 g:254 r:254 W:9 H:0 a:255 b:254 g:254 r:254 W:0 H:1 a:255 b:254 g:254 r:254 W:1 H:1 a:255 b:255 g:255 r:255
    • Excelente solución!!!! funcionó a la perfección
  3. 17

    Aquí está mi (bastante extenso) solución:

    public Bitmap Crop(Bitmap bmp)
    {
    int w = bmp.Width, h = bmp.Height;
    Func<int, bool> allWhiteRow = row =>
    {
    for (int i = 0; i < w; ++i)
    if (bmp.GetPixel(i, row).R != 255)
    return false;
    return true;
    };
    Func<int, bool> allWhiteColumn = col =>
    {
    for (int i = 0; i < h; ++i)
    if (bmp.GetPixel(col, i).R != 255)
    return false;
    return true;
    };
    int topmost = 0;
    for (int row = 0; row < h; ++row)
    {
    if (allWhiteRow(row))
    topmost = row;
    else break;
    }
    int bottommost = 0;
    for (int row = h - 1; row >= 0; --row)
    {
    if (allWhiteRow(row))
    bottommost = row;
    else break;
    }
    int leftmost = 0, rightmost = 0;
    for (int col = 0; col < w; ++col)
    {
    if (allWhiteColumn(col))
    leftmost = col;
    else
    break;
    }
    for (int col = w-1; col >= 0; --col)
    {
    if (allWhiteColumn(col))
    rightmost = col;
    else
    break;
    }
    int croppedWidth = rightmost - leftmost;
    int croppedHeight = bottommost - topmost;
    try
    {
    Bitmap target = new Bitmap(croppedWidth, croppedHeight);
    using (Graphics g = Graphics.FromImage(target))
    {
    g.DrawImage(bmp,
    new RectangleF(0, 0, croppedWidth, croppedHeight),
    new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
    GraphicsUnit.Pixel);
    }
    return target;
    }
    catch (Exception ex)
    {
    throw new Exception(
    string.Format("Values are topmost={0} btm={1} left={2} right={3}", topmost, bottommost, leftmost, rightmost),
    ex);
    }
    }
    • funciona perfectamente excepto cuando el croppedWidth o croppedHeight es cero, en este caso las puse a la bmp.Ancho o bmp.Altura respectivamente, y funciona como un encanto 🙂
    • hace este trabajo para cada imagen? como png, jpeg o gif?
    • sí, así debería ser, que todos se lea en un mapa de bits que proporciona por pixel a abordar. por favor, tenga en cuenta que esta solución no es muy rápida y hay mucho más rápido maneras de hacer esto (fuera de .NET)
  4. 9

    Necesitaba una solución que trabajó en imágenes de gran tamaño (GetPixel es lento), así que escribí el método de extensión de abajo. Parece que funciona bien en mi limitada de prueba. El inconveniente es que «Permiten Código Inseguro» tiene que ser revisado en su proyecto.

    public static Image AutoCrop(this Bitmap bmp)
    {
    if (Image.GetPixelFormatSize(bmp.PixelFormat) != 32)
    throw new InvalidOperationException("Autocrop currently only supports 32 bits per pixel images.");
    //Initialize variables
    var cropColor = Color.White;
    var bottom = 0;
    var left = bmp.Width; //Set the left crop point to the width so that the logic below will set the left value to the first non crop color pixel it comes across.
    var right = 0;
    var top = bmp.Height; //Set the top crop point to the height so that the logic below will set the top value to the first non crop color pixel it comes across.
    var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
    unsafe
    {
    var dataPtr = (byte*)bmpData.Scan0;
    for (var y = 0; y < bmp.Height; y++)
    {
    for (var x = 0; x < bmp.Width; x++)
    {
    var rgbPtr = dataPtr + (x * 4);
    var b = rgbPtr[0];
    var g = rgbPtr[1];
    var r = rgbPtr[2];
    var a = rgbPtr[3];
    //If any of the pixel RGBA values don't match and the crop color is not transparent, or if the crop color is transparent and the pixel A value is not transparent
    if ((cropColor.A > 0 && (b != cropColor.B || g != cropColor.G || r != cropColor.R || a != cropColor.A)) || (cropColor.A == 0 && a != 0))
    {
    if (x < left)
    left = x;
    if (x >= right)
    right = x + 1;
    if (y < top)
    top = y;
    if (y >= bottom)
    bottom = y + 1;
    }
    }
    dataPtr += bmpData.Stride;
    }
    }
    bmp.UnlockBits(bmpData);
    if (left < right && top < bottom)
    return bmp.Clone(new Rectangle(left, top, right - left, bottom - top), bmp.PixelFormat);
    return null; //Entire image should be cropped, so just return null
    }
    • Si usted está buscando sólo recorte de transparencia, modificar el «si ((cropColor.A > 0…) {» línea de arriba a la simple «if (a != 0) {«. Entonces usted puede deshacerse de la cropColor variable, así. Funciona muy bien hasta ahora.
    • ¿qué entiende usted por Permitir Código Inseguro
    • Usted puede obtener más información en la inseguro bandera y cómo se activa para el proyecto en Visual Studio en msdn.microsoft.com/en-us/library/ct597kb0.aspx. Básicamente permite el uso de punteros en un sentido tradicional con la aritmética y tal.
    • Exactamente lo que yo estaba buscando en un rápido cambio de proyecto. Gracias!
  5. 5

    Es ciertamente posible. En pseudocódigo:

    topmost = 0
    for row from 0 to numRows:
    if allWhiteRow(row): 
    topmost = row
    else:
    # found first non-white row from top
    break
    botmost = 0
    for row from numRows-1 to 0:
    if allWhiteRow(row): 
    botmost = row
    else:
    # found first non-white row from bottom
    break

    Y lo mismo para la izquierda y la derecha.

    El código para allWhiteRow implicaría mirar los píxeles de esa fila y asegurarse de que todos somos cerca de a 255,255,255.

  6. 3

    revisión restantes 1px espacio en blanco en la parte superior y a la izquierda

        public Bitmap Crop(Bitmap bitmap)
    {
    int w = bitmap.Width;
    int h = bitmap.Height;
    Func<int, bool> IsAllWhiteRow = row =>
    {
    for (int i = 0; i < w; i++)
    {
    if (bitmap.GetPixel(i, row).R != 255)
    {
    return false;
    }
    }
    return true;
    };
    Func<int, bool> IsAllWhiteColumn = col =>
    {
    for (int i = 0; i < h; i++)
    {
    if (bitmap.GetPixel(col, i).R != 255)
    {
    return false;
    }
    }
    return true;
    };
    int leftMost = 0;
    for (int col = 0; col < w; col++)
    {
    if (IsAllWhiteColumn(col)) leftMost = col + 1;
    else break;
    }
    int rightMost = w - 1;
    for (int col = rightMost; col > 0; col--)
    {
    if (IsAllWhiteColumn(col)) rightMost = col - 1;
    else break;
    }
    int topMost = 0;
    for (int row = 0; row < h; row++)
    {
    if (IsAllWhiteRow(row)) topMost = row + 1;
    else break;
    }
    int bottomMost = h - 1;
    for (int row = bottomMost; row > 0; row--)
    {
    if (IsAllWhiteRow(row)) bottomMost = row - 1;
    else break;
    }
    if (rightMost == 0 && bottomMost == 0 && leftMost == w && topMost == h)
    {
    return bitmap;
    }
    int croppedWidth = rightMost - leftMost + 1;
    int croppedHeight = bottomMost - topMost + 1;
    try
    {
    Bitmap target = new Bitmap(croppedWidth, croppedHeight);
    using (Graphics g = Graphics.FromImage(target))
    {
    g.DrawImage(bitmap,
    new RectangleF(0, 0, croppedWidth, croppedHeight),
    new RectangleF(leftMost, topMost, croppedWidth, croppedHeight),
    GraphicsUnit.Pixel);
    }
    return target;
    }
    catch (Exception ex)
    {
    throw new Exception(string.Format("Values are top={0} bottom={1} left={2} right={3}", topMost, bottomMost, leftMost, rightMost), ex);
    }
    }
  7. 1
    public void TrimImage() {
    int threshhold = 250;
    int topOffset = 0;
    int bottomOffset = 0;
    int leftOffset = 0;
    int rightOffset = 0;
    Bitmap img = new Bitmap(@"e:\Temp\Trim_Blank_Image.png");
    bool foundColor = false;
    //Get left bounds to crop
    for (int x = 1; x < img.Width && foundColor == false; x++)
    {
    for (int y = 1; y < img.Height && foundColor == false; y++)
    {
    Color color = img.GetPixel(x, y);
    if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
    foundColor = true;
    }
    leftOffset += 1;
    }
    foundColor = false;
    //Get top bounds to crop
    for (int y = 1; y < img.Height && foundColor == false; y++)
    {
    for (int x = 1; x < img.Width && foundColor == false; x++)
    {
    Color color = img.GetPixel(x, y);
    if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
    foundColor = true;
    }
    topOffset += 1;
    }
    foundColor = false;
    //Get right bounds to crop
    for (int x = img.Width - 1; x >= 1 && foundColor == false; x--)
    {
    for (int y = 1; y < img.Height && foundColor == false; y++)
    {
    Color color = img.GetPixel(x, y);
    if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
    foundColor = true;
    }
    rightOffset += 1;
    }
    foundColor = false;
    //Get bottom bounds to crop
    for (int y = img.Height - 1; y >= 1 && foundColor == false; y--)
    {
    for (int x = 1; x < img.Width && foundColor == false; x++)
    {
    Color color = img.GetPixel(x, y);
    if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
    foundColor = true;
    }
    bottomOffset += 1;
    }
    //Create a new image set to the size of the original minus the white space
    //Bitmap newImg = new Bitmap(img.Width - leftOffset - rightOffset, img.Height - topOffset - bottomOffset);
    Bitmap croppedBitmap = new Bitmap(img);
    croppedBitmap = croppedBitmap.Clone(
    new Rectangle(leftOffset - 3, topOffset - 3, img.Width - leftOffset - rightOffset + 6, img.Height - topOffset - bottomOffset + 6),
    System.Drawing.Imaging.PixelFormat.DontCare);
    //Get a graphics object for the new bitmap, and draw the original bitmap onto it, offsetting it do remove the whitespace
    //Graphics g = Graphics.FromImage(croppedBitmap);
    //g.DrawImage(img, 1 - leftOffset, 1 - rightOffset);
    croppedBitmap.Save(@"e:\Temp\Trim_Blank_Image-crop.png", ImageFormat.Png);
    }

    Tengo el código de otros post en ms, pero que ha errores, he cambiado algo, ahora funciona bien.

    El post de http://msm2020-sc.blogspot.com/2013/07/c-crop-white-space-from-around-image.html

Dejar respuesta

Please enter your comment!
Please enter your name here