Skip to content
Merged
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
26 changes: 26 additions & 0 deletions .github/workflows/cpp_linter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: C++ Linters

on:
push:
branches:
- '**'
pull_request:
branches:
- '**'

jobs:
cpp_linter:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Python Setup
uses: actions/setup-python@v3
with:
python-version: 3.13

- name: Install libraries
run: make python-install-development

- name: clang-tidy
run: make clang-tidy
34 changes: 0 additions & 34 deletions .github/workflows/cpp_linters.yaml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
name: Linters
on:
push:
branches:
- '**'
pull_request:
branches:
- '**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Python Setup
uses: actions/setup-python@v3
with:
python-version: 3.13
- name: Install libraries
run: make python-install-development
- name: mypy
run: make mypy
- name: ruff
run: make ruff
- name: flake8
run: make flake8
name: Python Linters

on:
push:
branches:
- '**'
pull_request:
branches:
- '**'

jobs:
python_linter:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Python Setup
uses: actions/setup-python@v3
with:
python-version: 3.13

- name: Install libraries
run: make python-install-development

- name: mypy
run: make mypy

- name: ruff
run: make ruff

- name: flake8
run: make flake8
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ python-install-development:
python-install-editable:
pip3 install -e .[development]

clang-tidy:
clang-tidy $(shell find ./src/cpp/ -name '*.hpp' -o -name '*.cpp') -- -I$(shell python3 -c "import sysconfig; print(sysconfig.get_path('include'))") -I$(shell python3 -c "import numpy; print(numpy.get_include())")

mypy:
mypy ./src/python/

Expand All @@ -18,4 +21,3 @@ ruff:

flake8:
flake8 ./src/python/

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# CPython-based Extension for Image Manipulation

[![C++ Linters](https://github.com/ForNeus57/advanced-python-programming-project/actions/workflows/cpp_linter.yaml/badge.svg)](https://github.com/ForNeus57/advanced-python-programming-project/actions/workflows/cpp_linter.yaml)
[![Python Linters](https://github.com/ForNeus57/advanced-python-programming-project/actions/workflows/python_linter.yaml/badge.svg)](https://github.com/ForNeus57/advanced-python-programming-project/actions/workflows/python_linter.yaml)

<!-- TOC -->
* [CPython-based Extension for Image Manipulation](#cpython-based-extension-for-image-manipulation)
* [Team members](#team-members)
Expand Down
28 changes: 19 additions & 9 deletions src/cpp/fast/foo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,39 @@ numpy_system(PyObject *self, PyObject *args)
int sts;

if (!PyArg_ParseTuple(args, "s", &command))
{
return NULL;
}

sts = system(command);
return PyLong_FromLong(sts);
}

static PyObject *
numpy_add(PyObject *self, PyObject *args){
numpy_add(PyObject *self, PyObject *args)
{
PyArrayObject *arr;
PyArg_ParseTuple(args, "O", &arr);
if(PyErr_Occurred()){
if(PyErr_Occurred())
{
return NULL;
}
if(!PyArray_Check(arr) || PyArray_TYPE(arr) != NPY_DOUBLE) {
if(!PyArray_Check(arr) || PyArray_TYPE(arr) != NPY_DOUBLE)
{
PyErr_SetString(PyExc_TypeError, "Argument must be a numpy array of type double!");
return NULL;
}


double *data = (double *) PyArray_DATA(arr);
double *data = reinterpret_cast<double *>(PyArray_DATA(arr));
int64_t size = PyArray_SIZE(arr);

double total=0;
for (int i=0; i < size; i++){
double total = 0;
for (int i = 0; i < size; i++)
{
total += data[i];
}

return PyFloat_FromDouble(total);
}

Expand All @@ -42,13 +50,15 @@ static PyObject *SpamError = NULL;
static int
numpy_module_exec(PyObject *m)
{
if (SpamError != NULL) {
if (SpamError != NULL)
{
PyErr_SetString(PyExc_ImportError,
"cannot initialize numpy module more than once");
return -1;
}
SpamError = PyErr_NewException("numpy.error", NULL, NULL);
if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0) {
if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0)
{
return -1;
}

Expand All @@ -62,7 +72,7 @@ static PyMethodDef numpy_methods[] = {
};

static PyModuleDef_Slot numpy_module_slots[] = {
{Py_mod_exec, (void*) numpy_module_exec},
{Py_mod_exec, reinterpret_cast<void*>(numpy_module_exec)},
{0, NULL}
};

Expand Down
16 changes: 14 additions & 2 deletions src/python/app/command/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ def get_parser() -> ArgumentParser:
subparser = parser.add_subparsers(required=True,
help='Command to be performed on an image')

operation_class: type[Operation]
for operation_class in [Rotate90Operation, IdentityOperation, FlipOperation, BGR2RGBOperation, RollOperation, GrayscaleOperation, HistogramEqualizationOperation]:
for operation_class in available_commands():
operation = operation_class()

operation_parser = subparser.add_parser(name=operation_class.name(),
Expand All @@ -46,6 +45,7 @@ def get_parser() -> ArgumentParser:

return parser


def prepare_command(command: Operation) -> Callable[[Namespace], int]:

def wrapper(args: Namespace) -> int:
Expand All @@ -61,3 +61,15 @@ def wrapper(args: Namespace) -> int:
return 0

return wrapper


def available_commands():
return [
Rotate90Operation,
IdentityOperation,
FlipOperation,
BGR2RGBOperation,
RollOperation,
GrayscaleOperation,
HistogramEqualizationOperation,
]
3 changes: 3 additions & 0 deletions src/python/app/imcli.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Main entrypoint for imcli program"""

from app.command.parser import get_parser


def main() -> None:
args = get_parser().parse_args()
return args.func(args)


if __name__ == '__main__':
main()
6 changes: 4 additions & 2 deletions src/python/app/io/bmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ def read_format(self, file: BinaryIO) -> Image:
assert panes == 1
assert bits_per_pixel in (1, 4, 8, 16, 24, 32)

compression_method, raw_bitmap_data_size, horizontal_resolution, vertical_resolution, num_colors, num_important_colors = \
compression_method, raw_bitmap_data_size, horizontal_resolution, vertical_resolution, \
num_colors, num_important_colors = \
struct.unpack('IIiiII', dib_header_no_size[12:])
assert compression_method == 0
assert raw_bitmap_data_size != 0
Expand All @@ -61,7 +62,8 @@ def read_format(self, file: BinaryIO) -> Image:
num_colors_end = bits_per_pixel // 8
return Image(data=np.flip(
np.flip(
np.frombuffer(bytes(image_bytes), dtype=np.uint8).reshape(image_height, image_width, num_colors_end)[:, :, ::-1],
np.frombuffer(bytes(image_bytes),
dtype=np.uint8).reshape(image_height, image_width, num_colors_end)[:, :, ::-1],
axis=0
),
axis=1)
Expand Down
6 changes: 3 additions & 3 deletions src/python/app/io/format_factory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import enum
from typing import Self

from app.error.unknown_format_exception import UnknownFormatException
from app.io.bmp import BMPReader, BMPWriter
Expand All @@ -11,7 +10,7 @@ class KnownFormat(enum.Enum):
BMP = 0

@classmethod
def from_string(cls, data_format: str) -> Self:
def from_string(cls, data_format: str) -> 'KnownFormat':
match data_format:
case 'bmp':
return cls.BMP
Expand All @@ -24,7 +23,7 @@ def get_available_formats(cls) -> list[str]:
return [e.name.lower() for e in cls]

@classmethod
def default(cls) -> Self:
def default(cls) -> 'KnownFormat':
return KnownFormat.BMP


Expand All @@ -37,6 +36,7 @@ def get_reader_from_format(data_format: KnownFormat) -> FormatReader:
case _:
assert False, "unreachable"


def get_writer_from_format(data_format: KnownFormat) -> FormatWriter:

match data_format:
Expand Down
2 changes: 0 additions & 2 deletions src/python/app/io/format_reader.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from abc import abstractmethod, ABC
from typing import BinaryIO

import numpy as np

from app.image.image import Image


Expand Down
2 changes: 1 addition & 1 deletion src/python/app/operation/histogram_equalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ def equalize_chanel(self, image: np.ndarray) -> np.ndarray:
cdf = (256 - 1) * cdf / cdf[-1]

image_equalized = np.interp(image.flatten(), bins[:-1], cdf)
return image_equalized.reshape(image.shape)
return image_equalized.reshape(image.shape)
3 changes: 0 additions & 3 deletions src/python/app/operation/operation.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from abc import ABC, abstractmethod
from argparse import Namespace, ArgumentParser
from typing import final

import numpy as np

from app.image.image import Image

Expand Down