• C#扫描图片去黑边


    最近项目遇到一个问题,需要对扫描出来的图片进行去除黑边。百度下没有找到处理黑边的源码,无奈自己尝试写了这个方法。

      1         /// <summary>
      2         /// 自动去除图像扫描黑边
      3         /// </summary>
      4         /// <param name="fileName"></param>
      5         public static void AutoCutBlackEdge(string fileName)
      6         {
      7             //打开图像
      8             Bitmap bmp = OpenImage(fileName);
      9 
     10             RemoveBlackEdge(bmp);
     11             //保存图像
     12             SaveImage(bmp, fileName);
     13         }
     14 
     15         private static byte[] rgbValues; // 目标数组内存
     16 
     17         /// <summary>
     18         /// 图像去黑边
     19         /// </summary>
     20         /// <param name="bmp"></param>
     21         /// <returns></returns>
     22         private static Bitmap RemoveBlackEdge(Bitmap bmp)
     23         {
     24             Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
     25             BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
     26 
     27             // 获取图像参数  
     28             int w = bmpData.Width;
     29             int h = bmpData.Height;
     30             int stride = bmpData.Stride;  // 扫描线的宽度 
     31             double picByteSize = GetPicByteSize(bmp.PixelFormat);
     32             int bWidth = (int)Math.Ceiling(picByteSize * w); //显示宽度
     33             int offset = stride - bWidth;  // 显示宽度与扫描线宽度的间隙  
     34             IntPtr ptr = bmpData.Scan0;   // 获取bmpData的内存起始位置  
     35             int scanBytes = stride * h;  // 用stride宽度,表示这是内存区域的大小
     36 
     37             // 分别设置两个位置指针,指向源数组和目标数组  
     38             int posScan = 0;
     39             rgbValues = new byte[scanBytes];  // 为目标数组分配内存  
     40             Marshal.Copy(ptr, rgbValues, 0, scanBytes);  // 将图像数据拷贝到rgbValues中  
     41 
     42             bool isPass = true;
     43             int i = 0, j = 0;
     44             int cutW = (int)(bWidth * 0.02); //2%宽度(可修改)
     45             int cutH = (int)(h * 0.02);      //2%高度(可修改)
     46             int posLen = (int)(picByteSize * 8); //继续查找深度为8的倍数(可修改)
     47             //左边
     48             for (i = 0; i < h; i++)
     49             {
     50                 for (j = 0; j < bWidth; j++)
     51                 {
     52                     isPass = true;
     53                     if (rgbValues[posScan] < 255) rgbValues[posScan] = 255;
     54 
     55                     if (rgbValues[posScan + 1] == 255)
     56                     {
     57                         for (int m = 1; m <= posLen; m++)
     58                         {
     59                             if (rgbValues[posScan + m] < 255) isPass = false;
     60                         }
     61                     }
     62                     if (rgbValues[posScan + 1] < 255 || bWidth / 2 < j) isPass = false;
     63                     recCheck(ref rgbValues, posScan, h, stride, true);
     64 
     65                     posScan++;
     66                     if (j >= cutW && isPass) break;
     67                 }
     68                 // 跳过图像数据每行未用空间的字节,length = stride - width * bytePerPixel  
     69                 if (j == bWidth) posScan += offset;
     70                 else posScan += (offset + bWidth - j - 1);
     71             }
     72             //右边
     73             posScan = scanBytes - 1;
     74             for (i = h - 1; i >= 0; i--)
     75             {
     76                 posScan -= offset;
     77                 for (j = bWidth - 1; j >= 0; j--)
     78                 {
     79                     isPass = true;
     80                     if (rgbValues[posScan] < 255) rgbValues[posScan] = 255;
     81 
     82                     if (rgbValues[posScan - 1] == 255)
     83                     {
     84                         for (int m = 1; m <= posLen; m++)
     85                         {
     86                             if (rgbValues[posScan - m] < 255) isPass = false;
     87                         }
     88                     }
     89                     if (rgbValues[posScan - 1] < 255 || bWidth / 2 > j) isPass = false;
     90                     recCheck(ref rgbValues, posScan, h, stride, false);
     91 
     92                     posScan--;
     93                     if (cutH < (h - i))
     94                         if (j < (bWidth - cutW) && isPass) break;
     95                 }
     96                 // 跳过图像数据每行未用空间的字节,length = stride - width * bytePerPixel
     97                 if (j != -1) posScan -= j;
     98             }
     99 
    100             // 内存解锁  
    101             Marshal.Copy(rgbValues, 0, ptr, scanBytes);
    102             bmp.UnlockBits(bmpData);  // 解锁内存区域  
    103 
    104             return bmp;
    105         }
    106 
    107         /// <summary>
    108         /// 上下去除黑边时,临近黑点去除
    109         /// </summary>
    110         /// <param name="rgbValues"></param>
    111         /// <param name="posScan"></param>
    112         /// <param name="h"></param>
    113         /// <param name="stride"></param>
    114         /// <param name="islLeft"></param>
    115         private static void recCheck(ref byte[] rgbValues, int posScan, int h, int stride, bool islLeft)
    116         {
    117             int scanBytes = h * stride;
    118             int cutH = (int)(h * 0.01); //临近最大1%高度(可修改)
    119             for (int i = 1; i <= cutH; i++)
    120             {
    121                 int befRow = 0;
    122                 if (islLeft && (posScan - stride * i) > 0)
    123                 {
    124                     befRow = posScan - stride * i;
    125                 }
    126                 else if (!islLeft && (posScan + stride * i) < scanBytes)
    127                 {
    128                     befRow = posScan + stride * i;
    129                 }
    130                 if (rgbValues[befRow] < 255) rgbValues[befRow] = 255;
    131                 else break;
    132             }
    133         }    
    View Code

    该方法没有涉及到什么算法,都是按自己的思路写的。虽然还是存在缺陷,但对于一般的文档黑边,还是够用的。欢迎大家进行扩展或修改,并多多指点。

    /// <summary>
    /// 根据图片像素数据格式获得占的字节大小
    /// </summary>
    /// <param name="bmpPixelFormat"></param>
    /// <returns></returns>
    private static double GetPicByteSize(PixelFormat bmpPixelFormat)
    {
    double picByteSize;
    if (bmpPixelFormat == PixelFormat.Format24bppRgb) picByteSize = 3;
    else if (bmpPixelFormat == PixelFormat.Format32bppArgb) picByteSize = 4;
    else if (bmpPixelFormat == PixelFormat.Format8bppIndexed) picByteSize = (double)3 / 24 * 8;
    else if (bmpPixelFormat == PixelFormat.Format1bppIndexed) picByteSize = (double)3 / 24;
    else if (bmpPixelFormat == PixelFormat.Format4bppIndexed) picByteSize = (double)3 / 24 * 4;
    else picByteSize = 3;

    return picByteSize;
    }

  • 相关阅读:
    盛京剑客系列21:再强调一遍:机会在MSCI成份,别走偏了
    盛京剑客系列20:平仓中兴通讯,获利45.51%,继续加仓优质个股
    盛京剑客系列19:推书《战胜华尔街》
    盛京剑客系列18:很多人因为恐惧脚下的小土坑,却丢掉了一米远处的大金矿
    盛京剑客系列17:市场暴跌下投资组合的调整
    盛京剑客系列16:推书《股市稳赚》
    盛京剑客系列15:割韭秘籍
    盛京剑客系列14:对高估值医药股要谨慎
    盛京剑客系列13:披露指数的密码,曙光就在前方
    leetcode -- Longest Valid Parentheses
  • 原文地址:https://www.cnblogs.com/fireshadow23/p/3600512.html
Copyright © 2020-2023  润新知