简体   繁体   中英

Pass a list from Python to C, list value not updated after process

Here is the sample C code:

#include <stdint.h>

struct Pixel {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    uint8_t a;
};

void normalize(struct Pixel *img, uint32_t num, uint8_t r_mean, uint8_t g_mean, uint8_t b_mean)
{
    for (uint32_t i = 0; i < num; ++i) {
        img[i].r -= r_mean;
        img[i].g -= g_mean;
        img[i].b -= b_mean;
    }
}

and the Python code:

import ctypes

class Pixel(ctypes.Structure):
    _fields_ = [('r', ctypes.c_ubyte),
        ('g', ctypes.c_ubyte),
        ('b', ctypes.c_ubyte),
        ('a', ctypes.c_ubyte)]


pixels = [Pixel(255, 255, 255, 0), Pixel(128, 128, 128, 0), Pixel(0, 128, 128, 0)]
pixels_num = len(pixels)
mean = 100

print('original pixels:', pixels)
for pixel in pixels:
    print(pixel.r, pixel.g, pixel.b, pixel.a)

obj = ctypes.CDLL('struct_example.so')

obj.normalize.argtypes = (ctypes.POINTER(Pixel), ctypes.c_uint, ctypes.c_ubyte, ctypes.c_ubyte, ctypes.c_ubyte)

array_type = Pixel * pixels_num
obj.normalize(array_type(*pixels), pixels_num, mean, mean, mean)

print('normalized pixels:', pixels)
for pixel in pixels:
    print(pixel.r, pixel.g, pixel.b, pixel.a)

I compile the C code with gcc struct_example.c -fPIC -shared -o struct_example.so , run the Python code and get the following result which is not what I expected:

original pixels: [<__main__.Pixel object at 0x7f66c17f8e18>, <__main__.Pixel object at 0x7f66c1486400>, <__main__.Pixel object at 0x7f66c1486620>]
255 255 255 0
128 128 128 0
0 128 128 0
normalized pixels: [<__main__.Pixel object at 0x7f66c17f8e18>, <__main__.Pixel object at 0x7f66c1486400>, <__main__.Pixel object at 0x7f66c1486620>]
255 255 255 0
128 128 128 0
0 128 128 0

What I expected to get is the pixels are changed at least. Did I missed something, what should I do if I want the list value changed after calling to C algorithms. Thanks in advance.

Listing [Python 3.Docs]: ctypes - A foreign function library for Python .

The pixels are changed in the .dll (or at least they should be - basing my statement on looking at the code only, as I didn't actually test it), but there's a logical error in the Python code:

  • When creating the ( CTypes ) array out of the list, the (inner) elements are copied not referenced , leaving the 2 containers totally decoupled
    • As a consequence, the array was created on the fly and passed to the function, the function modified the pixels inside, but the changes were not reflected on the initial list

code00.py :

#!/usr/bin/env python3

import sys
import ctypes as ct

class Pixel(ct.Structure):
    _fields_ = [
        ("r", ct.c_ubyte),
        ("g", ct.c_ubyte),
        ("b", ct.c_ubyte),
        ("a", ct.c_ubyte),
    ]

    def __str__(self):
        return "RGBa({0:d}, {1:d}, {2:d}, {3:d})".format(self.r, self.g, self.b, self.a)


def print_pixel(seq, index, header=None):
    text = "Sequence {0:s} element {1:d}:\n  {2:s} - {3:s}".format("({0:s})".format(header) if header else "", index, repr(seq[index]), str(seq[index]))
    print(text)


def main(*argv):
    pixel_list = [Pixel(255, 255, 255, 0), Pixel(128, 128, 128, 0), Pixel(0, 128, 128, 0)]
    PixelArray = Pixel * len(pixel_list)
    pixel_array = PixelArray(*pixel_list)
    pixel_index = 0

    print_pixel(pixel_list, pixel_index, "pixel_list")
    print_pixel(pixel_array, pixel_index, "pixel_array")

    pixel_array[pixel_index].r = pixel_array[pixel_index].g = pixel_array[pixel_index].b = 128  # Modify pixel(s) in the array

    print_pixel(pixel_list, pixel_index, "pixel_list")
    print_pixel(pixel_array, pixel_index, "pixel_array")

    #processed_pixel_list = [pixel for pixel in pixel_array]
    #print_pixel(processed_pixel_list, pixel_index, "processed_pixel_list")


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(*sys.argv[1:])
    print("\nDone.")

Output :

 e:\\Work\\Dev\\StackOverflow\\q059485131>c:\\Install\\pc064\\Python\\Python\\03.08.01\\python.exe code00.py Python 3.8.1 (tags/v3.8.1:1b293b6, Dec 18 2019, 23:11:46) [MSC v.1916 64 bit (AMD64)] 64bit on win32 Sequence (pixel_list) element 0: <__main__.Pixel object at 0x00000150C9628140> - RGBa(255, 255, 255, 0) Sequence (pixel_array) element 0: <__main__.Pixel object at 0x00000150C9628540> - RGBa(255, 255, 255, 0) Sequence (pixel_list) element 0: <__main__.Pixel object at 0x00000150C9628140> - RGBa(255, 255, 255, 0) Sequence (pixel_array) element 0: <__main__.Pixel object at 0x00000150C9628540> - RGBa(128, 128, 128, 0) Done.

As seen, it's possible to replicate the behavior without a .dll . To go around the problem, either:

  • Use the array (once it's created)
  • After done processing the array, create a list out of it (but I wouldn't see any benefits)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM