c#掃描圖片去黑邊(掃描儀去黑邊)


/// <summary>
??????? /// 自動去除圖像掃描黑邊
??????? /// </summary>
??????? /// <param name="fileName"></param>
??????? public static void AutoCutBlackEdge(string fileName)
??????? {
??????????? //打開圖像
??????????? Bitmap bmp = OpenImage(fileName);

?

??????????? RemoveBlackEdge(bmp);
??????????? //保存圖像
??????????? SaveImage(bmp, fileName);
??????? }

??????? private static byte[] rgbValues; // 目標數組內存

??????? /// <summary>
??????? /// 圖像去黑邊
??????? /// </summary>
??????? /// <param name="bmp"></param>
??????? /// <returns></returns>
??????? private static Bitmap RemoveBlackEdge(Bitmap bmp)
??????? {
??????????? Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
??????????? BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

??????????? // 獲取圖像參數?
??????????? int w = bmpData.Width;
??????????? int h = bmpData.Height;
??????????? int stride = bmpData.Stride;? // 掃描線的寬度
??????????? double picByteSize = GetPicByteSize(bmp.PixelFormat);
??????????? int bWidth = (int)Math.Ceiling(picByteSize * w); //顯示寬度
??????????? int offset = stride - bWidth;? // 顯示寬度與掃描線寬度的間隙?
??????????? IntPtr ptr = bmpData.Scan0;?? // 獲取bmpData的內存起始位置?
??????????? int scanBytes = stride * h;? // 用stride寬度,表示這是內存區域的大小

??????????? // 分別設置兩個位置指針,指向源數組和目標數組?
??????????? int posScan = 0;
??????????? rgbValues = new byte[scanBytes];? // 為目標數組分配內存?
??????????? Marshal.Copy(ptr, rgbValues, 0, scanBytes);? // 將圖像數據拷貝到rgbValues中?

??????????? bool isPass = true;
??????????? int i = 0, j = 0;
??????????? int cutW = (int)(bWidth * 0.02); //2%寬度(可修改)
??????????? int cutH = (int)(h * 0.02);????? //2%高度(可修改)
??????????? int posLen = (int)(picByteSize * 8); //繼續查找深度為8的倍數(可修改)
??????????? //左邊
??????????? for (i = 0; i < h; i++)
??????????? {
??????????????? for (j = 0; j < bWidth; j++)
??????????????? {
??????????????????? isPass = true;
??????????????????? if (rgbValues[posScan] < 255) rgbValues[posScan] = 255;

??????????????????? if (rgbValues[posScan + 1] == 255)
??????????????????? {
??????????????????????? for (int m = 1; m <= posLen; m++)
??????????????????????? {
??????????????????????????? if (rgbValues[posScan + m] < 255) isPass = false;
??????????????????????? }
??????????????????? }
??????????????????? if (rgbValues[posScan + 1] < 255 || bWidth / 2 < j) isPass = false;
??????????????????? recCheck(ref rgbValues, posScan, h, stride, true);

??????????????????? posScan++;
??????????????????? if (j >= cutW && isPass) break;
??????????????? }
??????????????? // 跳過圖像數據每行未用空間的字節,length = stride - width * bytePerPixel?
??????????????? if (j == bWidth) posScan += offset;
??????????????? else posScan += (offset + bWidth - j - 1);
??????????? }
??????????? //右邊
??????????? posScan = scanBytes - 1;
??????????? for (i = h - 1; i >= 0; i--)
??????????? {
??????????????? posScan -= offset;
??????????????? for (j = bWidth - 1; j >= 0; j--)
??????????????? {
??????????????????? isPass = true;
??????????????????? if (rgbValues[posScan] < 255) rgbValues[posScan] = 255;

??????????????????? if (rgbValues[posScan - 1] == 255)
??????????????????? {
??????????????????????? for (int m = 1; m <= posLen; m++)
??????????????????????? {
??????????????????????????? if (rgbValues[posScan - m] < 255) isPass = false;
??????????????????????? }
??????????????????? }
??????????????????? if (rgbValues[posScan - 1] < 255 || bWidth / 2 > j) isPass = false;
??????????????????? recCheck(ref rgbValues, posScan, h, stride, false);

??????????????????? posScan--;
??????????????????? if (cutH < (h - i))
??????????????????????? if (j < (bWidth - cutW) && isPass) break;
??????????????? }
??????????????? // 跳過圖像數據每行未用空間的字節,length = stride - width * bytePerPixel
??????????????? if (j != -1) posScan -= j;
??????????? }

??????????? // 內存解鎖?
??????????? Marshal.Copy(rgbValues, 0, ptr, scanBytes);
??????????? bmp.UnlockBits(bmpData);? // 解鎖內存區域?

??????????? return bmp;
??????? }

??????? /// <summary>
??????? /// 上下去除黑邊時,臨近黑點去除
??????? /// </summary>
??????? /// <param name="rgbValues"></param>
??????? /// <param name="posScan"></param>
??????? /// <param name="h"></param>
??????? /// <param name="stride"></param>
??????? /// <param name="islLeft"></param>
??????? private static void recCheck(ref byte[] rgbValues, int posScan, int h, int stride, bool islLeft)
??????? {
??????????? int scanBytes = h * stride;
??????????? int cutH = (int)(h * 0.01); //臨近最大1%高度(可修改)
??????????? for (int i = 1; i <= cutH; i++)
??????????? {
??????????????? int befRow = 0;
??????????????? if (islLeft && (posScan - stride * i) > 0)
??????????????? {
??????????????????? befRow = posScan - stride * i;
??????????????? }
??????????????? else if (!islLeft && (posScan + stride * i) < scanBytes)
??????????????? {
??????????????????? befRow = posScan + stride * i;
??????????????? }
??????????????? if (rgbValues[befRow] < 255) rgbValues[befRow] = 255;
??????????????? else break;
??????????? }
??????? }

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386575.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386575.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386575.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

已成功拿下字節、騰訊、脈脈offer,算法太TM重要了

一、背景介紹 從實用角度梳理一篇能夠幫大家快速掃盲的CMake基礎教程&#xff0c;也是對我目前負責項目的一次學習總結。既然選擇從項目實用性考慮&#xff0c;下面的講解內容可能并不一定完整&#xff0c;更多的是符合項目目前使用到的一些特性。 接下來正面回答這個問題&am…

SpringBoot2.0 Actuator 監控參數說明

主要內容更 監控參數說明 Maven坐標 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency><groupId>io.micrometer</groupId>&…

帶你一步一步深入Handler源碼,醍醐灌頂!

開頭 最近有粉絲反應&#xff0c;不想做安卓了&#xff0c;有朋友轉到前端了&#xff0c;安卓不行了&#xff0c;問我怎么辦&#xff1f; 自從RN&#xff0c;Weex這種跨平臺編程語言出來以后&#xff0c;安卓將死的言論總是不絕于耳。隨著頗有摧枯拉朽之勢Flutter的出現&…

Spring基于狀態機squirrel-foundation簡單使用

squirrel-foundation的一些使用方法在百度上資料還是比較少&#xff0c;我是根據以下三個大佬寫的文章借鑒的&#xff0c;在這里記錄一下。 1、squirrel-foundation-demo 2、Squirrel使用&#xff08;中文文檔&#xff09; 3、squirrel-foundation狀態機的使用細節 我在這里直接…

記得把每一次面試當做經驗積累,深夜思考

開頭 Android開發&#xff0c;假如開始沒有任何的開發經驗的話&#xff0c; 千萬不要著急&#xff0c;不要想著在短時間內就把一個語言學習好&#xff0c; 因為你之前沒有任何的學習經驗&#xff0c; 在這個過程中需要有耐心地學習完JAVA的基礎知識&#xff0c; 然后才開始踏上…

squirrel-foundation-demo

一個簡單的squirrel-foundation-demo 利用狀態機模擬一個訂單的支付過程。 squirrel-foundation沒有任何嚴重的依賴關系&#xff0c;因此基本上它應該是高度可嵌入的。squirrel-foundation沒有整合spring框架&#xff0c;所以首先要用spring集成squirrel-foundation。spring集成…

MongoDB學習目錄

MongoDB基礎篇 MongoDB 之 $ 關鍵字 python操作MongoDB 轉載于:https://www.cnblogs.com/yanzhi-1996/p/11095016.html

講的真透徹!還有人不知道什么是AndroidX的嗎?已拿offer入職

前言 春招已經接近尾聲了&#xff0c;不知道各位小伙伴有沒有收獲自己心儀的offer呢。筆者疫情被裁后在家LeetCode狂刷了800多題&#xff0c;加之自己以為工作總結的知識、經驗&#xff0c;系統化的整理了一下。在五一期間已經收獲了字節的offer。廢話不多說&#xff0c;下面是…

docker 啟動的 jenkins 中調用宿主機docker進行build

前言 期初有這個需求感覺就跟套娃一樣&#xff0c;你在docker 中調用docker&#xff0c;笑哭……這個也太逗了。 不過的確遇到了&#xff0c;因為jenkins 容器中沒有docker &#xff0c;所以在編譯 docker build 的時候 會出現 docker command 不存在。 好吧&#xff0c;解決他…

Codeforces 773D Perishable Roads 最短路 (看題解)

Perishable Roads 智商題&#xff0c; 不會啊。。 貼個官方題解 https://codeforces.com/blog/entry/51883 #include<bits/stdc.h> #define LL long long #define LD long double #define ull unsigned long long #define fi first #define se second #define mk make_p…

Rancher中的服務升級實驗

創建一個空的應用myAPP&#xff0c;在myAPP 應用中&#xff0c;創建一個服務nginx-test&#xff0c;包含2個容器副本&#xff0c;使用nginx:1.13.0鏡像。假設使用一段時期以后&#xff0c;nginx的版本升級到1.13.1了&#xff0c;如何將該服務的鏡像版本升級到新的版本&#xff…

該如何高效實用Kotlin?看這一篇就夠了!

前言 說起程序員人們的第一印象就是工資高、加班兇、話少錢多頭發少。再加上現在科技互聯網公司太吃香&#xff0c;bat、華為小米等公司程序員加班情況被廣泛傳播&#xff0c;程序員用生命在敲代碼的印象刻在了很多人的心里。 與其它行業一樣&#xff0c;凡是有高級和普通&…

apply()與call()

JavaScript中的每一個Function對象都有一個apply()方法和一個call()方法&#xff0c;它們的語法分別為&#xff1a; /*apply()方法*/ function.apply(thisObj[, argArray])/*call()方法*/ function.call(thisObj[, arg1[, arg2[, [,...argN]]]]); 它們各自的定義&#xff1a; a…

Java基于redis實現分布式鎖(SpringBoot)

前言 分布式鎖&#xff0c;其實原理是就是多臺機器&#xff0c;去爭搶一個資源&#xff0c;誰爭搶成功&#xff0c;那么誰就持有了這把鎖&#xff0c;然后去執行后續的業務邏輯&#xff0c;執行完畢后&#xff0c;把鎖釋放掉。 可以通過多種途徑實現分布式鎖&#xff0c;例如…

請談下Android消息機制,復習指南

談起Android框架體系架構&#xff0c;我先提個問&#xff1a;什么是Android框架體系架構 &#xff1f; Android系統構架是安卓系統的體系結構&#xff0c;android的系統架構和其操作系統一樣&#xff0c;采用了分層的架構&#xff0c;共分為四層&#xff0c;從高到低分別是And…

SVN Cannot merge into a working copy that has local modifications

我嘗試了 主支&#xff0c;分支都提交&#xff0c;但是依然無法合并。 最終&#xff0c;我在服務器上將分支刪除&#xff0c;然后主支在拷貝過去。 一&#xff0c;打開服務器資源 二&#xff0c;刪除分支 三&#xff0c;拷貝主支到分支 四&#xff0c;刷新分支&#xff0c;就能…

資深Android開發帶你入門Framework,再不刷題就晚了!

想要成為一名優秀的Android開發&#xff0c;你需要一份完備的知識體系&#xff0c;在這里&#xff0c;讓我們一起成長為自己所想的那樣。 本文參考了目前大部分 Android 應用啟動優化的方案&#xff0c;將大家的方案做一個匯總&#xff0c;如果你有這方面的需求&#xff0c;只…

K8S相關內容

常用工具&#xff1a;docker linux k8s kubeadm 概念 etcd 數據庫 類似redis api server 接口對外提供api 調用 可以命令 kubectl 或者 kube-proxy&#xff0c;能訪問etcd&#xff0c;事件總線 scheduler 調度決策的組件 掌握新的情況&#xff0c;進行決策及分布pod放在哪些n…

資深Android開發帶你入門Framework,架構師必備技能

開頭 先說一下我大概的情況吧。渣本畢業&#xff0c;工作已經有快兩年了&#xff0c;從高中就開始玩小破站。無論是學習還是日常放松都是在b站。大學主學的軟件技術專業&#xff0c;所以&#xff0c;進大學校門那一刻起&#xff0c;去上海bilibili工作就在心里埋下了種子。在學…

Java——線程鎖,死鎖,等待喚醒機制

一、線程鎖 線程安全問題 其實&#xff0c;線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作&#xff0c;而無寫操作&#xff0c;一般來說&#xff0c;這個全局變量是線程安全的&#xff1b;若有多個線程同時執行寫操作&#xff0c;…