您現在的位置是:首頁 > 垂釣
SuperMemo實踐閉環(3)-批次挖空制卡的操作
- 由 來自知乎的一隻小胖子 發表于 垂釣
- 2022-08-02
怎麼新增制卡
本文闡述了在不使用ImageOcclusionEditor的情況下,我們透過OpenCV實現批次圖片遮擋的效果。
Occlusion指令碼執行後的效果如圖所示
一. 執行環境-配置Python/指令碼/終端環境
PyCharm / Python3。9 / Open-CV
PyCharm / Python3。9 的配置不再詳細講解,你可以參考我之前的專欄文章。
PyCharm中使用Python3。9編譯器
安裝Open-CV的Python外掛
pip3 install opencv-python
解決找不到CV2 Module的問題:
。py檔案執行時找不到,要注意在執行環境配置中新增變數
解決執行時找不到CV2模組的問題
如果以上的方法還不能解決你的問題,請按這篇內容再配置一下:
用Pycharm執行後出現“No module named ‘cv2’”錯誤的終極解決方案
blog。csdn。net/lhw19931201/article/details/86545964
終端單獨執行找不到時,要
確保你Python3.9的site-packages下存在cv2的模組
終端找不到CV2模組要確保。so存在
Likey@Laptop pythonProject % python3 。/occlusionCard。py “你的圖片路徑”
指令碼生成的最終SM樣式網頁
二. 指令碼內容 - 如下指令碼生成多個Occlusion圖片至圖床(使用了PicGo)並返回多個圖片連結
OpenCV-Occlusion 指令碼提供如下,具體操作步驟為:
1.截圖後對要制卡區繪製矩形遮擋並儲存(注意保證矩形區透明度75%以上,而且能看到遮擋處的內容), 2.直接執行上面那條指令碼:
(
如下程式碼區也有使用方法的說明) 3.如果遮擋生成的圖片不對請酌情調整contourAreaValue引數
#!/usr/bin/python3# -*- coding: utf-8 -*-import numpy as npimport cv2 as cvimport osimport sysimport requestsimport jsonfrom pprint import pprint################################## 實現(opencv)批次生成挖空卡片,呼叫picGo上傳並返回連結# 作者:一隻小胖子# 版本:V0。1# 知乎:https://www。zhihu。com/people/lxf-8868# 使用方法:# 1。使用snipaste截圖,並用矩形工具描出實心遮擋區(注意:透明度為75%以上)# 2。執行python3 occlusionCard。py “你的圖片路徑(包括字尾名)”# 設定輪廓面積值,按效果自己調整,一般是1500-4000之間contourAreaValue = 2500################################## 設定putText函式字型font = cv。FONT_HERSHEY_SIMPLEX# 計算兩邊夾角額cos值def angle_cos(p0, p1, p2): d1, d2 = (p0 - p1)。astype(‘float’), (p2 - p1)。astype(‘float’) return abs(np。dot(d1, d2) / np。sqrt(np。dot(d1, d1) * np。dot(d2, d2)))# 合併圖片def merge_img(image1, image2): h1, w1, c1 = image1。shape h2, w2, c2 = image2。shape if c1 != c2: print(“channels NOT match, cannot merge”) return else: if w1 > w2: tmp = np。zeros([h2, w1 - w2, c1]) image3 = np。hstack([image2, tmp]) image3 = np。vstack([image1, image3]) elif w1 == w2: image3 = np。hstack([image1, image2]) else: tmp = np。zeros([h1, w2 - w1, c2]) image3 = np。hstack([image1, tmp]) image3 = np。vstack([image3, image2]) return image3# 查詢矩形輪廓def find_squares(filepath, flag): img = cv。imread(filepath) # 讀取圖片物件 gray = cv。cvtColor(img, cv。COLOR_BGR2GRAY) gray = cv。GaussianBlur(gray, (3, 3), 1) # 1 ret, th1 = cv。threshold(gray, 128, 255, 0) # cv。THRESH_OTSU) # 0,255,cv。THRESH_BINARY | cv。THRESH_OTSU 127, 255,0 # 開閉運算去除噪點 kernel = cv。getStructuringElement(cv。MORPH_ELLIPSE, (3, 3)) th1 = cv。morphologyEx(th1, cv。MORPH_OPEN, kernel) th1 = cv。morphologyEx(th1, cv。MORPH_CLOSE, kernel) binary = cv。Canny(th1, 50, 100) contours, _hierarchy = cv。findContours(binary, cv。RETR_EXTERNAL, cv。CHAIN_APPROX_SIMPLE) # cv。RETR_EXTERNAL cv。RETR_TREE print(“輪廓數量:%d” % len(contours)) # 輪廓遍歷 roi_list = [] contours_2 = [] for cnt in contours: # cnt_len = cv。arcLength(cnt, True) # 計算輪廓周長 # cnt = cv。approxPolyDP(cnt, 0。02 * cnt_len, True) # 多邊形逼近 # # 條件判斷逼近邊的數量是否為4,輪廓面積是否大於1000,檢測輪廓是否為凸的 # if cv。contourArea(cnt) > 4000 and cv。isContourConvex(cnt): if cv。contourArea(cnt) > contourAreaValue: # 獲取外接矩形的值 x, y, w, h = cv。boundingRect(cnt) roi_list。append((x, y, w, h, cnt)) contours_2。append({“h”: h, “cnt”: cnt}) print(“發現挖空[” + str(len(roi_list)) + “]處, contours數為[” + str(len(contours_2)) + “]”) squares_len = len(roi_list) filenameAtr = filepath。rsplit(“/”) for roi_idx in range(len(roi_list)): index = 0 if flag == 1: # 正面 (將回答問題為紅色遮擋,已回答的顯示答案,其它為藍色遮擋) img = cv。imread(filepath) for roi_list_ in roi_list: (x, y, w, h, cnt_obj) = roi_list_ M = cv。moments(cnt_obj) # 計算輪廓的矩 cx = int(M[‘m10’] / M[‘m00’]) cy = int(M[‘m01’] / M[‘m00’] + h * 0。5 + 20) # 輪廓重心下移0。5倍高度 cv。putText(img, (“#%d” % index), (cx, cy), font, 0。8, (0, 128, 0), 2, cv。LINE_AA) # 抗鋸齒 cv。drawContours(img, contours, 0, (255, 0, 255), 2) if index < roi_idx: index = index + 1 continue elif index == roi_idx: idx_red_col = (0, 0, 255) # 紅色 else: idx_red_col = (255, 0, 0) # 藍色 cv。rectangle(img, (x, y), (x + w, y + h), idx_red_col, -1) # -1 2 -1為填充 index = index + 1 cv_filename = filenameAtr[0] + str(roi_idx) + “_A_” + filenameAtr[-1] print(cv_filename) # cv。imshow(cv_filename, img) cv。imwrite(cv_filename, img) elif flag == 0: # 反面 img2 = cv。imread(filepath) for roi_list_ in roi_list: (x, y, w, h, cnt_obj) = roi_list_ M = cv。moments(cnt_obj) # 計算輪廓的矩 cx = int(M[‘m10’] / M[‘m00’]) cy = int(M[‘m01’] / M[‘m00’] + h * 0。5 + 20) # 輪廓重心下移0。5倍高度 cv。putText(img2, (“#%d” % index), (cx, cy), font, 0。8, (0, 128, 0), 2, cv。LINE_AA) # 抗鋸齒 cv。drawContours(img2, contours, 0, (255, 0, 255), 2) if index <= roi_idx: # 已回答和正要回答的顯示答案 index = index + 1 continue else: idx_red_col = (255, 0, 0) # 藍色 cv。rectangle(img2, (x, y), (x + w, y + h), idx_red_col, -1) # -1 2 -1為填充 index = index + 1 cv_filename = filenameAtr[0] + str(roi_idx) + “_B_” + filenameAtr[-1] print(cv_filename) # cv。imshow(cv_filename, img2) cv。imwrite(cv_filename, img2) # cnt_len = cv。arcLength(cnt, True) # 計算輪廓周長 # cnt = cv。approxPolyDP(cnt, 0。02 * cnt_len, True) # 多邊形逼近 # # 條件判斷逼近邊的數量是否為4,輪廓面積是否大於1000,檢測輪廓是否為凸的 # if len(cnt) == 4 and cv。contourArea(cnt) > 1000 and cv。isContourConvex(cnt): # M = cv。moments(cnt) # 計算輪廓的矩 # cx = int(M[‘m10’] / M[‘m00’]) # cy = int(M[‘m01’] / M[‘m00’]) # 輪廓重心 # # cnt = cnt。reshape(-1, 2) # max_cos = np。max([angle_cos(cnt[i], cnt[(i + 1) % 4], cnt[(i + 2) % 4]) for i in range(4)]) # # 只檢測矩形(cos90° = 0) # if max_cos < 0。1: # # True # # 檢測四邊形(不限定角度範圍) # # if True: # # if True: # index = index + 1 # cv。putText(img, (“#%d” % index), (cx, cy), font, 0。7, (255, 0, 255), 2) # squares。append(cnt) return squares_len# 邏輯執行入口def main(img_file_path): file_dir_path = os。path。dirname(img_file_path) + “/” file_img_name = os。path。basename(img_file_path) file_upload_list = [] # 待上傳的圖片列表 print(“1。開始生成正面圖像”) squares_len = find_squares(img_file_path, 1) print(“2。開始生成反面影象”) squares_len = find_squares(img_file_path, 0) print(“3。開始合成生成的圖象”) for file_obj_idx in range(squares_len): # print(“{}{}{}{}”。format(file_dir_path, file_obj_idx, “_A_”, file_img_name)) # print(“{}{}{}{}”。format(file_dir_path, file_obj_idx, “_B_”, file_img_name)) view1 = cv。imread(“{}{}{}{}”。format(file_dir_path, file_obj_idx, “_A_”, file_img_name)) view2 = cv。imread(“{}{}{}{}”。format(file_dir_path, file_obj_idx, “_B_”, file_img_name)) # 迭加圖片模式 # cv。addWeighted(view1, alpha, src2, beta, gamma, dst=None, dtype=None) # alpha/beta 對應兩張圖片的透明度, 0是完全透明 1是完全不透明 # overlapping = cv。addWeighted(view1, 0。8, view2, 0。2, 0) # 合併的檔名 file_out_name = “{}{}_QA_{}”。format(file_dir_path, file_obj_idx, file_img_name) print(file_out_name) # 只上傳合併後的QA圖片 file_upload_list。append(file_out_name) # 水平或垂直合併圖片 view3 = merge_img(view1, view2) # cv。imshow(‘view3’, view3) cv。imwrite(file_out_name, view3) # # Exit if ESC pressed # # cv。waitKey() k = cv。waitKey(100) & 0xff # 100ms if k == 27: cv。destroyAllWindows() # exit() # 獲取圖片儲存目錄待上傳圖片 print(‘圖片處理生成至: {} 結束!’。format(file_dir_path)) print(“開始上傳圖片,請確保你已提前配置好picGo上傳環境。。。”) # # 獲取傳入路徑下的: 當前目錄, 子目錄列表, 檔案列表 # for f_path, dir_names, f_names in os。walk(file_dir_path): # # 正常的圖片檔案,關鍵字查詢過濾檔名 # f_names = [f_name for f_name in f_names if not f_name。startswith(“。”) and f_name。__contains__(“。jpg”) # and os。path。isfile(f_name) and str(f_name)。__contains__(“#”)] # # 得到全路徑資訊 # f_names = [os。path。join(f_path, f_name) for f_name in f_names] # break # 只處理根目錄檔案 print(“待處理上傳圖片列表: {}”。format(file_upload_list)) url = “http://127。0。0。1:36677/upload” payload = json。dumps({ “list”: file_upload_list }) headers = { ‘Content-Type’: ‘application/json’ } response = requests。request(“POST”, url, headers=headers, data=payload) print(“PicGo上傳返回結果:”) print(response) obj_json_list = json。loads(response。text) pprint(obj_json_list) # 獲取圖片的URL連結 if “result” in obj_json_list。keys(): url_for_sm18 = [] # 格式化成SuperMemo網頁樣式 for url_ in obj_json_list[“result”]: url_str = “{}
”。format(file_img_name, url_) url_for_sm18。append(url_str) print(“上傳成功,返回SuperMemo樣式:”) # $fileName
print(“”。join(url_for_sm18)) else: print(“上傳失敗,請檢查是否重複上傳!”)# ——————開始呼叫方法——————-# 透過終端傳參呼叫img_path = str(sys。argv[1])print(“開始處理傳入的圖片:{}”。format(img_path))main(img_file_path=img_path)# pycharm直接執行# if __name__ == ‘__main__’:# # print(__doc__)# img_path = “/Users/Likey/PycharmProjects/pythonProject/pic/33。jpg”# main(img_path)
如果你用的是程式碼,下面的第三步驟可以不用再做了,程式碼已經實現了這個功能,第三步驟適合不用程式碼,直接手工操作的場景,總體上會麻煩一點....,建議你直接用程式碼的方式更方便.
三. Occlusion圖片批次上傳伺服器並返回連結(這一步可以用上面第二步的程式碼替換了....)
(
軟體自帶了一個網頁連結,但這裡我們要另外配置一個自定義連結用於獲取批次的圖片網址.)
先自定義返回圖片的連結格式
透過以上的指令碼生成了多個挖空Occlusion圖片後,我們
按需選中多個圖片拖至PicGo軟體的主介面進行上傳
,
上傳完後透過自定義的網頁形式,批次獲取圖片網頁連結.
主介面批次上傳多圖至服務端
獲取批次多圖的自定義格式連結
# 多圖的自定義返回連結QA_2_444
QA_1_444
QA_6_444
QA_7_444
20210228124446
四. 按以上方式透過程式碼或者手工獲取到多個連結後,我們就可以直接在SuperMemo中處理了.
匯入批次圖片連結並分割為子問答
批次轉換子問答及新增模版
多圖片連結制卡的最終效果
一隻小胖子:SuperMemo實踐閉環(4)-互動式處理網頁材料
2 贊同 · 2 評論
文章
本文結束....
我是一隻熱愛學習的小胖子,如果你也熱愛學習,並且對SuperMemo感興趣,歡迎轉發和評論!