圖像增強 目標檢測 仿射變換 圖像處理 扭曲圖像

1.背景

在目標檢測中,需要進行圖像增強。這里的代碼模擬了旋轉、扭曲圖像的功能,并且在扭曲的時候,能夠同時把標注的結果也進行扭曲。

這里忽略了讀取xml的過程,假設圖像IMG存在對應的標注框,且坐標為左上、右下兩個點,這立刻喲把IMG進行扭曲,并且盡可能保證標簽的坐標點也隨之扭曲。

2.效果

我有一張鐵塔的照片(原圖略,原圖是一張角度非常正確的圖片),處理后效果如下圖所示,可以看到圖像出現了扭曲(出現了黑邊),并且圖像右側中間部分的紅色絕緣子旁邊出現了兩個小白點(為了便于觀察而添加的),說明起到了扭曲的作用,并且大概率能保證坐標也被正確變化。
在這里插入圖片描述

3.動機

設計這種數據增強策略,主要出于以下兩種考慮:

  1. 在實際使用中,我發現即便是yolov8這樣的模型,對于扭曲的圖像(例如扭曲的筆記本),識別效果會有非常嚴重的下降,而真實情況中非常有可能出現扭曲(例如有人把筆記本半合上),模型需要識別。在電力場景中,也可能出現拍攝角度異常等情況。
  2. 在訓練中,將不同角度的目標展現給模型,也可能提高模型的效果。例如上圖中的絕緣子,比原圖要“矮胖”一些,因此可以算作“新樣本”,為模型提供更豐富的數據來源。

4.具體實現

第一步是圖像扭曲。在這一步中,代碼里的trapezoid_vertices表示你希望把圖像的哪一部分進行扭曲,target_trapezoid_vertices表示你希望把trapezoid_vertices扭曲到什么位置。你可以把代碼中相關位置的generate_random_cut和generate_random_perspective都去掉,然后就能明白了。

第二步是轉化原始標注的框。在使用gpt生成代碼的時候,提示詞將任務劃分為了5個步驟,提示詞如下

假設原始圖像PIC寬度為W,高度為H。在圖像標注場景中,我會告訴你兩個點special_points,分別代表原始的標注框的左上角和右下角。你需要執行以下步驟:
【步驟1】根據special_points,恢復原始的標注框的四個坐標。注意保留這四個點的順序關系。
【步驟2】:利用自定義函數fun1計算出變化之后的四個點的新坐標。
【步驟3】依次判斷四個點所連成的線段是否與原始圖像PIC的邊緣有交點,如果有,就將這些交點的坐標存到point_list中。
【步驟4】判斷變換后的四個點有哪些位于原始圖像PIC的范圍內,如果有,則將這些點的坐標存到point_list中。
【步驟5】分析point_list中的所有點,找出一個能完整包含這些點的矩形R,返回這個矩形R的左上角和右下角。

輔助代碼包含了前面提到的5個步驟,具體如下:

import random
import cv2
import numpy as np# 原始圖像剪裁的范圍
max_cut = 0.1
min_cut = 0.05# 扭曲的最大和最小程度
max_perspective = 0.1
min_perspective = 0.05# 制定初始剪裁范圍,你可以選擇從某些地方開始剪裁你的圖像
def generate_random_cut(x):# 計算 x 的 10% 和 20%min_value = min_perspective * xmax_value = max_perspective * x# 生成一個在 10% 到 20% 范圍內的隨機數random_value = random.uniform(min_value, max_value)# 隨機決定這個數是正數還是負數sign = random.choice([-1, 1])return sign * random_value# 制定目標梯形的范圍
def generate_random_perspective(x):# 計算 x 的 10% 和 20%min_value = min_perspective * xmax_value = max_perspective * xrandom_value = random.uniform(min_value, max_value)# 隨機決定這個數是正數還是負數sign = random.choice([-1, 1])return sign * random_value# 根據輸入的兩個點的坐標,復原出連續的矩形框的四個點坐標
def get_bbox_corners(special_points):top_left = special_points[0][0]bottom_right = special_points[0][1]top_right = [bottom_right[0], top_left[1]]bottom_left = [top_left[0], bottom_right[1]]return np.array([top_left, top_right, bottom_right, bottom_left])# line_intersection所需要的輔助函數
def on_segment(p, q, r):if (q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) andq[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1])):return Truereturn False# line_intersection所需要的輔助函數
def orientation(p, q, r):val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1])if val == 0:return 0  # collinearelif val > 0:return 1  # clockwiseelse:return 2  # counterclockwise# line_intersection所需要的輔助函數
def segments_intersect(p1, q1, p2, q2):o1 = orientation(p1, q1, p2)o2 = orientation(p1, q1, q2)o3 = orientation(p2, q2, p1)o4 = orientation(p2, q2, q1)if o1 != o2 and o3 != o4:return Trueif o1 == 0 and on_segment(p1, p2, q1):return Trueif o2 == 0 and on_segment(p1, q2, q1):return Trueif o3 == 0 and on_segment(p2, p1, q2):return Trueif o4 == 0 and on_segment(p2, q1, q2):return Truereturn False# 判斷四個點所連成的線段是否與原始圖像的邊緣有交點
def line_intersection(p1, p2, edge_start, edge_end):if not segments_intersect(p1, p2, edge_start, edge_end):return Nonexdiff = (p1[0] - p2[0] + 0.01, edge_start[0] - edge_end[0] + 0.01)ydiff = (p1[1] - p2[1] + 0.01, edge_start[1] - edge_end[1] + 0.01)def det(a, b):return a[0] * b[1] - a[1] * b[0]div = det(xdiff, ydiff)if div == 0:return Noned = (det(p1, p2), det(edge_start, edge_end))x = det(d, xdiff) / divy = det(d, ydiff) / divreturn x, y# 判斷四個點所連成的線段是否與原始圖像的邊緣有交點
def check_intersections(bbox_corners, w, h):edges = [((0.01, 0), (w, 0.01)),((w, 0.01), (w, h)),((w, h), (0.01, h)),((0.01, h), (0.01, 0.01))]point_list = []for i in range(len(bbox_corners)):p1 = bbox_corners[i]p2 = bbox_corners[(i + 1) % len(bbox_corners)]for edge in edges:intersection = line_intersection(p1, p2, edge[0], edge[1])if intersection:point_list.append(intersection)return point_list# 判斷變換后的四個點是否位于原始圖像范圍內
def check_points_within_image(bbox_corners, w, h):point_list = []for point in bbox_corners:if 0 <= point[0] <= w and 0 <= point[1] <= h:point_list.append(point)return point_list# 分析所有點,找出一個能完整包含這些點的矩形
def get_bounding_box(point_list):min_x = min(point[0] for point in point_list)max_x = max(point[0] for point in point_list)min_y = min(point[1] for point in point_list)max_y = max(point[1] for point in point_list)return (min_x, min_y), (max_x, max_y)

使用方法如下:

# 數據增強 - 拉伸
if __name__ == "__main__":# 定義兩個特殊點,即原始標注框的左上和右下special_points = [[3400, 1655],  # Example points, replace with your own[4550, 2350]]# 讀取圖片image_path = r'D:\data\拉伸原始圖像.jpg'image = cv2.imread(image_path)# 獲取圖像寬度和高度height, width = image.shape[:2]# 定義頂點坐標,你會保留這四個點之內的圖像,以便進行后續步驟trapezoid_vertices = np.array([[0 + generate_random_cut(width), 0 + generate_random_cut(height)],[width + generate_random_cut((width)), 0 + generate_random_cut((height))],[width + generate_random_cut(width), height + generate_random_cut(height)],[0 + generate_random_cut(width), height + generate_random_cut(height)]],dtype=np.float32)# 定義目標圖像的頂點坐標,會將trapezoid_vertices所保留的圖像拉伸,拉伸到target_trapezoid_vertices所對應的范圍內target_trapezoid_vertices = np.array([[0 + generate_random_perspective(width), 0 + generate_random_perspective(height)],[width + generate_random_perspective(width), 0 + generate_random_perspective(height)],[width + generate_random_perspective(width),height + generate_random_perspective(height)],[0 + generate_random_perspective(width), height + generate_random_perspective(height)]],dtype=np.float32)# 計算透視變換矩陣,用于將image進行拉伸perspective_matrix = cv2.getPerspectiveTransform(trapezoid_vertices, target_trapezoid_vertices)# 進行透視變換trapezoid_image = cv2.warpPerspective(image, perspective_matrix, (width, height))# 將坐標框進行處理,利用perspective_matrix得到新的坐標框的左上角和右下角special_points = np.array(special_points, dtype='float32')special_points = np.array([special_points])transformed_special_points = cv2.perspectiveTransform(special_points, perspective_matrix)# 得到新的標注的框的四個頂點的坐標bbox_corners = get_bbox_corners(transformed_special_points)# 如果有超出圖像邊界的點,就計算與圖像邊界的交點,保存到point_list中。point_list = check_intersections(bbox_corners, width, height)# 把留在圖像范圍內的點也加到point_list中point_list.extend(check_points_within_image(bbox_corners, width, height))# 得出新的理想中的標注框的坐標if point_list:rect = get_bounding_box(point_list)else:rect = Noneprint(len(rect))# 就愛那個新的標注框的左上角和右下角繪制出來,以便判斷是否正確for point in rect:x = tuple([int(i) for i in point])cv2.circle(trapezoid_image, x, 15, (256, 256, 256), 10)# 保存變換后的圖像save_path = r'D:\data\拉伸結果.jpg'cv2.imwrite(save_path, trapezoid_image)

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

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

相關文章

[C++初階]vector的初步理解

一、標準庫中的vector類 1.vector的介紹 1. vector是表示可變大小數組的序列容器 &#xff0c; 和數組一樣&#xff0c;vector可采用的連續存儲空間來存儲元素。也就是意味著可以采用下標對vector的元素進行訪問&#xff0c;和數組一樣高效。但是又不像數組&#xff0c;它的大…

災后恢復與勒索恢復的區別

災難恢復通常側重于物理基礎設施&#xff0c;如硬盤和網絡設備&#xff0c;而勒索軟件恢復則涉及數據完整性和防范網絡威脅。 BackBox 產品管理副總裁 Amar Ramakrishnan表示&#xff0c;在災難中&#xff0c;企業可能面臨硬件受損等問題&#xff0c;但在網絡安全事件中&#…

Java學習高級一

修飾符 static 類變量的應用場景 成員方法的分類 成員變量的執行原理 成員方法的執行原理 Java之 main 方法 類方法的常見應用場景 代碼塊 設計模式 單例設計模式 餓漢式單例設計模式 懶漢式單例設計模式 繼承 權限修飾符

小紅書 達芬奇:生活問答 AI 機器人

小紅書去年 9 月開始內測的生活問答 AI 機器人&#xff1a;達芬奇&#xff0c;現在可以在小紅書 APP 上用了 得益于小紅書平臺的特性&#xff0c;該助手擅長吃、住、寵、喝、學等等各類生活知識&#xff0c;目前還在搞活動&#xff0c;寫評測筆記最高得 666 元

為什么不能在foreach中刪除元素

文章目錄 快速失敗機制&#xff08;fail-fast&#xff09;for-each刪除元素為什么報錯原因分析邏輯分析 如何正確的刪除元素remove 后 breakfor 循環使用 Iterator 總結 快速失敗機制&#xff08;fail-fast&#xff09; In systems design, a fail-fast system is one which i…

合肥高新區建設世界領先科技園區政策(商務部分)申報獎勵補貼和條件材料、時間指南

一、合肥高新區建設世界領先科技園區政策&#xff08;商務部分&#xff09;申報主體 &#xff08;更多政策可以查看小編主頁方式&#xff09; 工商、稅務、統計關系均在合肥高新區&#xff0c;并在高新區持續經營。申請項目在高新區內實施、且符合政策要求的具有獨立法人資格…

網絡基礎:EIGRP

EIGRP&#xff08;Enhanced Interior Gateway Routing Protocol&#xff09;是由思科開發的一種高級距離矢量路由協議&#xff0c;結合了距離矢量和鏈路狀態路由協議的優點&#xff1b;EIGRP具有快速收斂、高效帶寬利用、負載均衡等特點&#xff0c;適用于各種規模的網絡。EIGR…

python sklearn機械學習-數據預處理

&#x1f308;所屬專欄&#xff1a;【機械學習】?作者主頁&#xff1a; Mr.Zwq??個人簡介&#xff1a;一個正在努力學技術的Python領域創作者&#xff0c;擅長爬蟲&#xff0c;逆向&#xff0c;全棧方向&#xff0c;專注基礎和實戰分享&#xff0c;歡迎咨詢&#xff01; 您…

【設計模式】策略模式(定義 | 特點 | Demo入門講解)

文章目錄 定義策略模式的結構 QuickStart | DemoStep1 | 策略接口Step2 | 策略實現Step3 | 上下文服務類Step4 | 客戶端 策略模式的特點優點缺點 定義 策略模式Strategy是一種行為模式&#xff0c;它能定義一系列算法&#xff0c;并將每種算法分別放入到獨立的類中&#xff0c…

書籍表達式得到期望結果的組成種數

題目 給定一個只由0(假)、1(真)、&(邏輯與)、|(邏輯或)和^(異或)五種組成的字符串express&#xff0c;再給定一個布爾值desired。返回express能有多少種組合方式。可以達到desired的結果。 舉例 express“1^0|0|1”,desiredfalse. 只有1^((0|0)|1)和1^(0|(0|1))的組合可…

負載均衡類型和算法解析

假如你正在設計和開發一個分布式服務系統&#xff0c;系統中存在一批能夠獨立運行的服務&#xff0c;而在部署上也采用了集群模式以防止出現單點故障。所謂集群&#xff0c;就是指將多個服務實例集中在一起&#xff0c;對外提供同一業務功能&#xff0c;也就是任意請求都可以由…

吉利銀河L6 AQS空氣質量監控系統

結論 頂配才有AQS 開啟空調且auto模式 則默認開啟AQS 無法關閉AQS AQS的作用 銀河L6 AQS觸發 和 圖標 AQS官方配置參數 官方文檔 吉利用戶手冊

開源即正義,3D軟件Blender設計指南

在當今數字化時代&#xff0c;開源軟件的崛起不僅代表著技術的發展&#xff0c;更象征著一種信息自由和技術民主的理念。其本質是集眾人之智&#xff0c;共同去完善一個軟件&#xff0c;最終使雙方互惠共贏。具體來說&#xff0c;開源的價值&#xff0c;在于打破資源壟斷&#…

Spring的事務管理、AOP實現底層

目錄 spring的事務管理是如何實現的&#xff1f; Spring的AOP的底層實現原理 spring的事務管理是如何實現的&#xff1f; 首先&#xff0c;spring的事務是由aop來實現的&#xff0c;首先要生成具體的代理對象&#xff0c;然后按照aop的整套流程來執行具體的操作邏輯&#xf…

NLP - 基于bert預訓練模型的文本多分類示例

項目說明 項目名稱 基于DistilBERT的標題多分類任務 項目概述 本項目旨在使用DistilBERT模型對給定的標題文本進行多分類任務。項目包括從數據處理、模型訓練、模型評估到最終的API部署。該項目采用模塊化設計&#xff0c;以便于理解和維護。 項目結構 . ├── bert_dat…

蘋果AI的國產大模型之爭,沒有懸念

文 | 智能相對論 作者 | 陳泊丞 蘋果終于公布了最新的AI進程。 一個月前&#xff0c;正如此前預期的那樣&#xff0c;人工智能是今年 WWDC 發布會的焦點。全程105分鐘的主題演講&#xff0c;就有40多分鐘用于介紹蘋果的AI成果。 蘋果似乎還有意玩了一把“諧音梗”&#xff…

用機器改變人類方向

1800 世紀初&#xff0c;美國迎來了工業革命&#xff0c;這是一個由技術進步推動的變革時代。新機器和制造技術的引入重塑了經濟格局&#xff0c;提高了生產效率&#xff0c;同時減少了某些領域對手工勞動的需求。因此&#xff0c;這種轉變導致了失業。 如今&#xff0c;我們看…

實現點擊按鈕導出頁面pdf

在Vue 3 Vite項目中&#xff0c;你可以使用html2canvas和jspdf庫來實現將頁面某部分導出為PDF文檔的功能。以下是一個簡單的實現方式&#xff1a; 1.安裝html2canvas和jspdf&#xff1a; pnpm install html2canvas jspdf 2.在Vue組件中使用這些庫來實現導出功能&#xff1a;…

統計信號處理基礎 習題解答11-11

題目 考慮矢量MAP估計量 證明這個估計量對于代價函數 使貝葉斯風險最小。其中&#xff1a;, &#xff0c;且. 解答 貝葉斯風險函數&#xff1a; 基于概率密度的非負特性&#xff0c;上述對積分要求最小&#xff0c;那就需要內層積分達到最小。令內層積分為&#xff1a; 上述積…

蘋果Mac電腦能玩什么游戲 Mac怎么運行Windows游戲

相對于Windows平臺來說&#xff0c;Mac電腦可玩的游戲較少。雖然蘋果設備的性能足以支持各種大型游戲&#xff0c;但由于系統以及蘋果配套服務的限制&#xff0c;很多游戲無法在Mac系統中運行。不過&#xff0c;借助虛擬機軟件&#xff0c;Mac電腦可以突破系統限制玩更多的游戲…