opencv 減少圖片顏色數量,python 用 ctypes 呼叫 C dll , OSError: exception: access violation reading 0x00000276A3EEB130
項目 | 版本規格 |
---|---|
win10 | x64 21H1 |
python | 3.9.2 |
gcc(MinGW) | 10.2.0 |
opencv | 4.5.2 |
想要減少圖片中所出現的顏色數,用 HSV 色域調整,該過程要極短時間完成。
這裡有三個版本
第一個是原版,可以正常使用但真的有點慢
1k 圖片處理要花上 7.8s
第二個是用 numpy 迭代改良版
1k 圖片要處理 3.9s
但三個版本是打算用 c dll + ctypes 加速
但是一直有問題
第一版
import time
import ctypes
import datetime
import cv2
import numpy as np
# ⇓要嘗試的話這裡要改一下路徑,隨意一張圖即可。
frame = cv2.imread("./material/ttt.png")
max_type_quantity = 256
h_type_quantity = 16
s_type_quantity = 16
v_type_quantity = 16
_wn = 'HSV_assort'
photo_number = 0
h_par = int(max_type_quantity / h_type_quantity)
s_par = int(max_type_quantity / s_type_quantity)
v_par = int(max_type_quantity / v_type_quantity)
hsv_f = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hsv_f_1D = np.reshape(hsv_f, (hsv_f.shape[0] * hsv_f.shape[1], 3))
# ⇓最主要要加速的地方
for i in hsv_f_1D:
i[0] = round(i[0] / h_par) * h_par
i[1] = round(i[1] / s_par) * s_par
i[2] = round(i[2] / v_par) * v_par
# ⇑最主要要加速的地方
frame = cv2.cvtColor(frame, cv2.COLOR_HSV2BGR)
cv2.imshow(_wn, frame)
key = cv2.waitKey(0)
if key == ord('q'):
pass
elif key == ord('g'):
st = datetime.datetime.fromtimestamp(
time.time()).strftime('%Y%m%d_%H%M%S')
imwrite_path = f'./{st}_nb{photo_number}.jpg'
print('get_photo. ' + imwrite_path)
cv2.imwrite(imwrite_path, frame)
photo_number += 1
cv2.destroyAllWindows()
第二版(替換第一版部分)
# ⇓最主要要加速的地方
n = 0
with np.nditer(hsv_f_1D, op_flags=['readwrite']) as it:
for i in it:
# print(i[...])
if n == 0:
i[...] = int(i / h_par) * h_par
n = 1
elif n == 1:
i[...] = int(i / s_par) * s_par
n = 2
elif n == 2:
i[...] = int(i / v_par) * v_par
n = 0
# ⇑最主要要加速的地方
第三版(1/2 - C 的部分)
#include <stdio.h>
#include <stdlib.h>
void hsv_assort(int *matrix, int rows, int cols, int h_par, int s_par, int v_par){
int i, j;
printf("rows = %d\n", rows);
printf("columns = %d\n", cols);
printf("h_par = %d\n", h_par);
printf("s_par = %d\n", s_par);
printf("v_par = %d\n", v_par);
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
if (j==0) {
matrix[i * rows + j] = matrix[i * rows + j] / h_par * h_par;
} else if (j==1) {
matrix[i * rows + j] = matrix[i * rows + j] / s_par * s_par;
} else {
matrix[i * rows + j] = matrix[i * rows + j] / v_par * v_par;
}
}
}
printf("hsv_assort end");
}
編譯指令:gcc -shared hsv_assort_accelerate.c -o hsv_assort_accelerate.dll
第三版(2/2 - Python 的部分)
import time
import ctypes
import datetime
import cv2
import numpy as np
# 參考自 https://blog.csdn.net/MCANDML/article/details/80426914
def Convert1DToCArray(TYPE, ary):
arow = TYPE(*ary.tolist())
return arow
def Convert2DToCArray(ary):
ROW = ctypes.c_int * len(ary[0])
rows = []
for i in range(len(ary)):
rows.append(Convert1DToCArray(ROW, ary[i]))
MATRIX = ROW * len(ary)
return MATRIX(*rows)
def ShowCArrayArray(caa):
for row in caa:
for col in row:
print(col)
frame = cv2.imread("./material/ttt.png")
dll = ctypes.CDLL("./hsv_assort_accelerate.dll")
max_type_quantity = 256
h_type_quantity = 16
s_type_quantity = 16
v_type_quantity = 16
_wn = 'HSV_assort'
photo_number = 0
h_par = int(max_type_quantity / h_type_quantity)
s_par = int(max_type_quantity / s_type_quantity)
v_par = int(max_type_quantity / v_type_quantity)
hsv_f = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hsv_f_1D = np.reshape(hsv_f, (hsv_f.shape[0] * hsv_f.shape[1], 3))
rows = hsv_f_1D.shape[0]
cols = hsv_f_1D.shape[1]
hsv_f_1D = hsv_f_1D.astype('int32')
caa = Convert2DToCArray(hsv_f_1D)
# ⇓ 嘗試無果,註解掉
# dll.hsv_assort.argtypes = [ctypes.c_int, ctypes.c_int,
# ctypes.c_int, ctypes.c_int,
# ctypes.c_int, ctypes.c_int]
# dll.hsv_assort.restype = None
dll.hsv_assort(caa,
rows, cols,
h_par, s_par, v_par)
print("dll.hsv_assort END")
print(np.array(caa))
frame = np.reshape(np.array(caa), frame.shape)
frame = cv2.cvtColor(frame, cv2.COLOR_HSV2BGR)
cv2.imshow(_wn, frame)
key = cv2.waitKey(0)
if key == ord('q'):
pass
elif key == ord('g'):
st = datetime.datetime.fromtimestamp(
time.time()).strftime('%Y%m%d_%H%M%S')
imwrite_path = f'./{st}_nb{photo_number}.jpg'
print('get_photo. ' + imwrite_path)
cv2.imwrite(imwrite_path, frame)
photo_number += 1
cv2.destroyAllWindows()
##結果
這裡執行下去很奇怪,可以發現理論上要嘛直接噴錯,要嘛成功執行(會有 opencv 視窗)
但是他卻是在跑 C 的 dll 時突然結束(沒有執行到 printf("hsv_assort end");
)
然後持續執行幾次後會隨機出現記憶體位置出錯
在 stackoverflow 上有一些人有出現類似的,結論好像是說要注意要輸入 ctypes 的型別
所以我其實有把加 hsv_f_1D = hsv_f_1D.astype('int32')
看看能不能轉成符合規則的
還有改動 函式- Convert2DToCArray
的 ROW = ctypes.c_int * len(ary[0])
改成其他 ctypes
但發現這應該沒有用。
不過有的弔詭的是
我其實有先做最小實現
最小實現程式碼(1/2 - C 部分)
#include <stdio.h>
void show_matrix(int * matrix, int rows, int columns){
int i, j;
for (i = 0; i < rows; i++) {
for (j = 0; j < columns; j++) {
matrix[i * rows + j] = matrix[i * rows + j] + 1;
printf("matrix[%d][%d] = %d\n", i, j, matrix[i * rows + j]);
}
}
}
最小實現程式碼(2/2 - Python 部分)
# from ctypes import *
import ctypes
import numpy as np
def Convert1DToCArray(TYPE, ary):
arow = TYPE(*ary.tolist())
return arow
def Convert2DToCArray(ary):
ROW = ctypes.c_int * len(ary[0])
rows = []
for i in range(len(ary)):
rows.append(Convert1DToCArray(ROW, ary[i]))
MATRIX = ROW * len(ary)
return MATRIX(*rows)
def ShowCArrayArray(caa):
for row in caa:
for col in row:
print(col)
# a = np.array([[1, 2, 2], [1, 3, 4]], dtype=np.uint8)
a = np.array([[1, 2, 2], [1, 3, 4]])
type(a[0][0])
# ary = a
caa = Convert2DToCArray(a)
ShowCArrayArray(caa)
a.shape
lib = ctypes.cdll.LoadLibrary("./t1.dll")
lib.show_matrix(caa, a.shape[0], a.shape[1])
print('======')
print(a)
# print(caa)
print(np.array(caa))
# 結果有變化
# [[2 3 4]
# [2 4 4]]
再看了 2 次感覺上也沒有跟我那個差太多
但是這個有修改到 Orz...
我預期的效果是 每個陣列內的值+1
但是 row 1,col 3 的值變+2(2->4)
但是 row 2,col 3 的值變+0(4->4)
##求問各位
謝謝你看到這裡 m(_ _)m