iT邦幫忙

0

opencv 減少圖片顏色數量,python 用 ctypes 呼叫 C dll , OSError: exception: access violation reading

標題

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");)
然後持續執行幾次後會隨機出現記憶體位置出錯
https://ithelp.ithome.com.tw/upload/images/20210601/20112304wqnEeq1tEK.png

在 stackoverflow 上有一些人有出現類似的,結論好像是說要注意要輸入 ctypes 的型別
所以我其實有把加 hsv_f_1D = hsv_f_1D.astype('int32') 看看能不能轉成符合規則的
還有改動 函式- Convert2DToCArrayROW = 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]]

https://ithelp.ithome.com.tw/upload/images/20210601/201123046ONl9DP0uX.png
再看了 2 次感覺上也沒有跟我那個差太多
但是這個有修改到 Orz...

然後寫問題到這裡的當下發現怪怪的...

我預期的效果是 每個陣列內的值+1
但是 row 1,col 3 的值變+2(2->4)
但是 row 2,col 3 的值變+0(4->4)

##求問各位

  1. 最小實現程式碼的部分出了什麼問題 Orz....
  2. 第三版要怎麼修改才會正常呢?
  3. 第二版的 numpy 方案還有辦法修改更快嗎?

謝謝你看到這裡 m(_ _)m

尚未有邦友回答

立即登入回答