Skip to content
This repository was archived by the owner on Sep 28, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ accimage mimics the PIL API and can be used as a backend for

Operations implemented:

- `Image(path)` - creates an `accimage.Image` from a path to a jpeg file
- For example: `img = accimage.Image('chicago.jpg')`
- `Image(bytes)` - creates an `accimage.Image` from the bytes of a jpeg file loaded into memory
- For example: `f = open('chicago.jpg', 'rb'); b = f.read(); img = accimage.Image(b)`
- `Image.resize((width, height))`
- `Image.crop((left, upper, right, lower))`
- `Image.transpose(PIL.Image.FLIP_LEFT_RIGHT)`
Expand Down
3 changes: 2 additions & 1 deletion accimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ typedef struct {

void image_copy_deinterleave(ImageObject* self, unsigned char* output_buffer);
void image_copy_deinterleave_float(ImageObject* self, float* output_buffer);
void image_from_jpeg(ImageObject* self, const char* path);
void image_from_jpeg_path(ImageObject* self, const char* path);
void image_from_jpeg_memory(ImageObject* self, const char* inmem, unsigned long inmem_size);
void image_resize(ImageObject* self, int new_height, int new_width, int antialiasing);
void image_flip_left_right(ImageObject* self);

Expand Down
20 changes: 17 additions & 3 deletions accimagemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,28 @@ static PyTypeObject Image_Type = {
0, /*tp_is_gc*/
};

#if PY_MAJOR_VERSION == 2
#define PyUnicode_AsUTF8 PyString_AsString
#define PyUnicode_Check PyString_Check
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is going to work as strings in python 2 were bytes by default: b'hi' == 'hi'. This is why travis is failing at the moment. I'm not sure what we should do--drop python 2 support completely? (Although if torchvision is still supporting 2, so should accimage). If we need to support 2, perhaps we could ask for a bytesio object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix it, i think I can make the in-memory option be a kwarg-only option probably.

#endif

static int Image_init(ImageObject *self, PyObject *args, PyObject *kwds) {
static char* argnames[] = { "path", NULL };
const char *path;

if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", argnames, &path))
PyObject *obj;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", argnames, &obj))
return -1;

image_from_jpeg(self, path);
if (PyUnicode_Check(obj)) {
// if object is string
const char* path = PyUnicode_AsUTF8(obj);
image_from_jpeg_path(self, path);
} else if (PyBytes_Check(obj)) {
// object is bytes in memory
const char* memory = PyBytes_AsString(obj);
Py_ssize_t size = PyBytes_Size(obj);
image_from_jpeg_memory(self, memory, size);
}

return PyErr_Occurred() ? -1 : 0;
}
Expand Down
46 changes: 36 additions & 10 deletions jpegloader.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,47 @@ static void accimage_jpeg_error_exit(j_common_ptr cinfo) {
longjmp(accimage_err->setjmp_buffer, 1);
}


void image_from_jpeg(ImageObject* self, const char* path) {
void _image_from_jpeg(ImageObject* self, unsigned int input_type, const char* input, unsigned long length) {
/*
input_type == 0: input is a `path` string to the jpeg file (example: "foo/a.jpg"
- in this case, the `length` argument is unused
input_type == 1: input is a pointer to file contents loaded into memory
- in this case, `length` is the length of the contents in memory in bytes
*/
struct jpeg_decompress_struct state = { 0 };
struct accimage_jpeg_error_mgr jpeg_error;
FILE* file = NULL;
unsigned char* buffer = NULL;

file = fopen(path, "rb");
if (file == NULL) {
PyErr_Format(PyExc_IOError, "failed to open file %s", path);
if (input_type == 0) {
file = fopen(input, "rb");
if (file == NULL) {
PyErr_Format(PyExc_IOError, "failed to open file %s", input);
goto cleanup;
}
}

state.err = jpeg_std_error(&jpeg_error.pub);
jpeg_error.pub.error_exit = accimage_jpeg_error_exit;
if (setjmp(jpeg_error.setjmp_buffer)) {
char error_message[JMSG_LENGTH_MAX];
(*state.err->format_message)((j_common_ptr) &state, error_message);
PyErr_Format(PyExc_IOError, "JPEG decoding failed: %s in file %s",
error_message, path);

if (input_type == 0) {
PyErr_Format(PyExc_IOError, "JPEG decoding failed: %s in file %s",
error_message, input);
} else { /* input_type == 1 */
PyErr_Format(PyExc_IOError, "JPEG decoding failed: %s on buffer",
error_message);
}
goto cleanup;
}

jpeg_create_decompress(&state);
jpeg_stdio_src(&state, file);
if (input_type == 0) {
jpeg_stdio_src(&state, file);
} else { /* input_type == 1 */
jpeg_mem_src(&state, (unsigned char*) input, length);
}
jpeg_read_header(&state, TRUE);

state.dct_method = JDCT_ISLOW;
Expand Down Expand Up @@ -83,7 +98,18 @@ void image_from_jpeg(ImageObject* self, const char* path) {

free(buffer);

if (file != NULL) {
if (input_type == 0) {
if (file != NULL) {
fclose(file);
}
}
}


void image_from_jpeg_path(ImageObject* self, const char* path) {
_image_from_jpeg(self, 0, path, 0);
}

void image_from_jpeg_memory(ImageObject* self, const char* inmem, unsigned long inmem_size) {
_image_from_jpeg(self, 1, inmem, inmem_size);
}
14 changes: 14 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ def test_reading_image():
assert image.width == 1920
assert image.height == 931

def test_reading_image_from_memory():
def get_img(inp):
image = accimage.Image(inp)
image_np = np.empty([image.channels, image.height, image.width], dtype=np.uint8)
image.copyto(image_np)
return image_np

path = 'chicago.jpg'
with open(path, 'rb') as f:
imbytes = f.read()

bytes_img = get_img(imbytes)
path_img = get_img(path)
assert np.alltrue(np.equal(bytes_img, path_img))

def test_resizing():
image = accimage.Image("chicago.jpg")
Expand Down