파이썬으로 가져온 이미지를 필터링하는 모듈을 C 언어로 만들었습니다.

직장에서 파이썬을 사용하여 이미지 처리를 할 필요가 있습니다. OpenCV등으로 할 수 있는 범위라면 그것으로 좋지만, 독자 알고리즘의 필터는 C/C++가 아니면 실용적인 속도가 나오지 않습니다. 그래서 파이썬 용 모듈을 C 언어로 만들려고했습니다.

빨리 시도하고 싶은 분은 나의 리포지토리를 이용해 주세요.
htps : // 기주 b. 코 m / 소라 미미 / py 모즈 ゜ 이마 게 ぃ l r

파이썬 용 자체 제작 모듈을 만드는 방법



종속 모듈 설치



첫 번째 시도는 helloworld이므로 불필요하지만, 나중의 이미지 처리 편에서는 numpy, pillow, matplotlib를 사용하고 있으므로 이러한 모듈이 설치되어 있어야합니다. Ubuntu에서 개발한다고 가정합니다. 파이썬 개발 패키지가 필요합니다.
sudo apt install python3-dev python3-matplotlib

함수 정의 구조체 작성


static PyMethodDef myMethods[] = {
    { "helloworld", helloworld, METH_NOARGS, "My helloworld function." },
    { NULL }
};

정의의 내용은 {(함수 이름), (함수에 대한 포인터), (인수 전달 방법), (설명문)}의 순서로 기술합니다.
METH_NOARGS 는 인수가 없는 함수의 경우입니다. 인수를 이용하려면 METH_VARARGS 로 합니다.

모듈 정의 구조체 작성


static struct PyModuleDef mymodule = {
    PyModuleDef_HEAD_INIT,
    "mymodule",
    "Python3 C API Module",
    -1,
    myMethods
};

모듈 이름이나 설명 등을 씁니다.

모듈을 초기화하는 함수 작성


PyMODINIT_FUNC PyInit_mymodule(void)
{
    import_array();
    return PyModule_Create(&mymodule);
}
import_array() 라는 함수 호출은 필요에 따라 작성됩니다. 이번 테마에서는, 화상 처리로 배열을 취급하기 때문에 이것이 필요합니다. 배열을 사용하지 않으면 필요하지 않습니다. 배열 API를 사용하는 모든 소스 코드에서 import_array()를 실행해야합니다.

함수 본문 작성


static PyObject *helloworld(PyObject *self, PyObject *args)
{
    fprintf(stderr, "Hello, world\n");
    return Py_None;
}

첫 번째 평가판 함수이므로 helloworld 입니다. 나중에 이미지 처리를 수행하는 코드를 구현할 것입니다.

빌드용 스크립트 작성



setup.py
from distutils.core import setup, Extension
setup(name = 'mymodule', version = '1.0.0', ext_modules = [Extension('mymodule', ['mymodule.c'])])

빌드


python3 setup.py build_ext -i
긴 이름의 .so 파일이 성공하면 성공합니다.
$ ls *.so
mymodule.cpython-35m-x86_64-linux-gnu.so

호출 (Python 측) 프로그램 작성



main.py
import mymodule

mymodule.helloworld()

실행하다



Python 프로그램과 모듈 .so 파일을 같은 디렉토리에 넣은 상태에서 실행합니다.
$ python3 main.py 
Hello, world

이미지 처리 필터 만들기



예를 들면, 세피아조의 필터를 걸는 함수로 합니다. 함수 이름이 helloworld에서 sepia로 변경되었습니다.

파이썬 측의 프로그램은 다음과 같습니다.

main.py
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import mymodule

im = np.array(Image.open('kamo.jpg'))

im = mymodule.sepia(im)

print(type(im))
print(im.dtype)
print(im.shape)

plt.imshow(im)
plt.show()

다음과 같이 작동합니다.
  • 이미지 파일로드
  • 필터링
  • 배열 정보 표시
  • 이미지 표시

  • 함수 정의를 다음과 같이 합니다.
        { "sepia", sepia, METH_VARARGS, "Sepia tone image filter" },
    

    C 언어측 소스의 발췌입니다.

    mymodule.c
    static PyObject *sepia(PyObject *self, PyObject *args)
    {
        PyArrayObject *srcarray;
        if (!PyArg_ParseTuple(args, "O", &srcarray)) {
            fprintf(stderr, "invalid argument\n");
            return Py_None;
        }
    

    최초의 (라고 해도 1개만) 인수는 배열입니다. 객체로 가져옵니다.

    입력의 배열은, (높이)×(폭)×(채널)의 3차원 배열이므로, 그렇지 않으면 에러로 합니다. 채널수는 3(RGB)로 한정합니다.
        if (srcarray->nd != 3) {
            fprintf(stderr, "invalid image\n");
            return Py_None;
        }
        if (srcarray->dimensions[2] != 3) {
            fprintf(stderr, "invalid image\n");
            return Py_None;
        }
    

    이미지의 크기를 가져옵니다.
        int h = srcarray->dimensions[0];
        int w = srcarray->dimensions[1];
    

    필터 처리 결과의 이미지를 포함하는 배열을 확보합니다.
        npy_intp dims[] = { h, w, 3 };
        PyObject *image = PyArray_SimpleNew(3, dims, NPY_UBYTE);
        if (!image) {
            fprintf(stderr, "failed to allocate array\n");
            return Py_None;
        }
    

    (높이)×(너비)×(채널)의 순서를 틀리지 마십시오.

    배열로서 확보한 오브젝트는, 그대로 배열 구조체에의 포인터에 캐스트 할 수 있습니다.
        PyArrayObject *dstarray = (PyArrayObject *)image;
    

    필터링합니다.
        for (int y = 0; y < h; y++) {
            uint8_t const *src = (uint8_t const *)srcarray->data + y * w * 3;
            uint8_t *dst = (uint8_t *)dstarray->data + y * w * 3;
            for (int x = 0; x < w; x++) {
                uint8_t r = src[x * 3 + 0];
                uint8_t g = src[x * 3 + 1];
                uint8_t b = src[x * 3 + 2];
                r = pow(r / 255.0, 0.62) * 205 + 19;
                g = pow(g / 255.0, 1.00) * 182 + 17;
                b = pow(b / 255.0, 1.16) * 156 + 21;
                dst[x * 3 + 0] = r;
                dst[x * 3 + 1] = g;
                dst[x * 3 + 2] = b;
            }
        }
    

    개체를 반환 종료합니다.
        return image;
    }
    

    이상입니다.

    오류 처리는 생략되었습니다. 잘못된 이미지 파일을 받았을 때와 같이 함수가 None
    처음에도 썼습니다만, 모든 소스 코드는 여기 로 공개하고 있습니다.

    좋은 웹페이지 즐겨찾기