Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0c00192
chore: init gradle project
vicitori Sep 15, 2025
6b7f38d
chore: add simple work plan
vicitori Sep 15, 2025
fe7c503
chore: add arch
vicitori Sep 17, 2025
dc45a3d
feat: simple io
vicitori Sep 17, 2025
cdaee82
feat: update IOService
vicitori Sep 18, 2025
e48e3fa
feat: start logic in ImageProcessingService
vicitori Sep 18, 2025
012be5a
feat: picocli dependency, program start
vicitori Sep 18, 2025
1655056
chore: clean up build.gradle.kts
vicitori Sep 18, 2025
aacbaac
chore: clean up arch
vicitori Sep 18, 2025
1961ab2
feat: arch for filters
vicitori Sep 18, 2025
df2907c
chore: apply new filters
vicitori Sep 18, 2025
bc61ce0
feat: implement sequential convolution
vicitori Sep 18, 2025
0856218
feat: add templates for other convolutions
vicitori Sep 18, 2025
7593518
chore: delete uninformative comments
vicitori Sep 18, 2025
c05f8c2
feat: add more filters and picture
vicitori Sep 18, 2025
d3cc810
chore: recombine project, rename classes
vicitori Sep 21, 2025
4863f38
feat: carry out the main convolution algo part in abstract class
vicitori Sep 21, 2025
3662d04
feat: add updated convolution by pixels and rows, with ExecutorService
vicitori Sep 21, 2025
c0ce24d
feat: convolution with a fixed number of rectangles
vicitori Sep 21, 2025
81fbfff
chore: fix fmt
vicitori Sep 21, 2025
25c3b4d
chore: add comment
vicitori Sep 21, 2025
a1ed628
feat: convolution by column
vicitori Sep 21, 2025
6106150
feat: add image processing pipeline
vicitori Sep 22, 2025
88ee27c
chore: upd .gitignore
vicitori Sep 22, 2025
12f139a
feat: extend for pipeline processing
vicitori Sep 22, 2025
cd88e66
chore: delete unused method
vicitori Sep 22, 2025
6fb52fa
feat: add samples
vicitori Sep 22, 2025
8803ffc
feat: add more interesting filters
vicitori Sep 22, 2025
cebfd6c
ci: add check tests, linter, release
vicitori Sep 22, 2025
8bf58d6
fix: endless loop
vicitori Sep 22, 2025
38b71d9
feat: add tests
vicitori Sep 22, 2025
8afafeb
ci: delete checkstyle
vicitori Sep 22, 2025
bbcc463
chore: update configs for gradle
vicitori Sep 22, 2025
a83af77
ci: add new linter and change java version
vicitori Sep 22, 2025
cfa5880
fix: linter warnings
vicitori Sep 22, 2025
8bfbee6
fix: add gradle-wrapper.jar
vicitori Sep 22, 2025
91b01e8
feat: add readme
vicitori Sep 22, 2025
fdb0f42
chore: edit --dir flad
vicitori Sep 22, 2025
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
12 changes: 12 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf

# These are Windows script files and should use crlf
*.bat text eol=crlf

# Binary files should be left untouched
*.jar binary

93 changes: 93 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI/CD Pipeline

on:
push:
branches: [ main, work ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup Java
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'

- name: Run tests
run: ./gradlew test

- name: Run SpotBugs
run: ./gradlew spotbugsMain

- name: Build
run: ./gradlew build

- name: Create fat JAR
run: ./gradlew fatJar

- name: Upload JAR artifact
uses: actions/upload-artifact@v4
with:
name: image-filtering-app
path: app/build/libs/image-filtering-all.jar
retention-days: 30

release:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'

steps:
- uses: actions/checkout@v3

- name: Setup Java
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'

- name: Build and create fat JAR
run: ./gradlew fatJar

- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ github.run_number }}
name: Image Filtering App v${{ github.run_number }}
body: |
## Image Filtering Application

Automated build from commit ${{ github.sha }}

## Usage
```bash
java -jar image-filtering-all.jar <input_path> <filter_name> [convolution_mode] [output_path]
```

## Available Filters
- blur
- emboss
- find_edges
- glass_distortion
- motion_blur
- negative
- pixelate
- radial_blur

## Convolution Modes
- sequential (default)
- row
- column
- pixel
- grid
files: app/build/libs/image-filtering-all.jar
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,14 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*

# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build

.idea

.DS_Store

104 changes: 103 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,103 @@
# image-filtering
### Image Filtering Application

A Java application for applying various image filters using different convolution algorithms.

---

### Project Structure

```
app/src/main/java/com/vicitori/
├── app/
│ ├── Main.java # Application entry point
│ ├── ImageFilteringApp.java # CLI interface using Picocli
│ ├── FilteringEngine.java # Main processing engine
│
├── core/
│ ├── Filter.java # Filter record with kernel, factor, bias
│ ├── Convolution.java # Abstract convolution interface
│ ├── AbstractConvolution.java # Base convolution implementation
│ └── conv/
│ ├── SequentialConvolution.java
│ ├── RowConvolution.java
│ ├── ColumnConvolution.java
│ ├── PixelConvolution.java
│ └── GridConvolution.java
├── filters/
│ └── FiltersLibrary.java
├── io/
│ └── ImageIO.java
└── pipeline/
└── ImagePipeline.java # Multi-threaded batch processing
```

### How to Run

#### Prerequisites
- Download the latest `image-filtering-all.jar` from the Releases page
- Java 21 or higher

#### Single Image Processing
```bash
java -jar image-filtering-all.jar <input_path> <filter_name> [convolution_mode] [output_path]
```

#### Batch Processing (Images in directory)
```bash
java -jar image-filtering-all.jar -d <input_directory> <filter_name> [convolution_mode] [output_directory] [-t threads]
```

### Command Line Options
- `-d, --directory` - Enable directory mode for batch processing
- `-t, --threads` - Number of worker threads (default: 4)
- `-h, --help` - Show help message

### Available Filters

- `blur` - gaussian blur
- `emboss` - emboss effect
- `find_edges` - edge detection
- `glass_distortion` - glass distortion effect
- `motion_blur` - motion blur
- `negative` - negative/invert colors
- `pixelate` - pixelation effect
- `radial_blur` - radial blur effect

### Convolution Modes

- `sequential` (default) - Standard sequential processing
- `row` - Row-based parallel processing
- `column` - Column-based parallel processing
- `pixel` - Pixel-by-pixel processing
- `grid` - Grid-based parallel processing

### Usage

```bash
# Apply blur filter to single image
java -jar image-filtering-all.jar input.jpg blur output.jpg

# Apply emboss filter with row convolution
java -jar image-filtering-all.jar input.jpg emboss row output.jpg

# Apply edge detection filter
java -jar image-filtering-all.jar input.jpg find_edges output.jpg

# Apply negative effect
java -jar image-filtering-all.jar input.jpg negative output.jpg

# Apply motion blur effect
java -jar image-filtering-all.jar input.jpg motion_blur output.jpg

# Batch process directory with 4 threads
java -jar image-filtering-all.jar -d ./images blur sequential ./output -t 4
```

### Building

```bash
./gradlew build
./gradlew fatJar
# image-filtering-all.jar will be at app/build/libs
```

54 changes: 54 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import org.gradle.internal.classpath.Instrumented.systemProperty

plugins {
application
id("com.github.spotbugs") version "6.0.14"
}

repositories {
mavenCentral()
}

dependencies {
implementation("info.picocli:picocli:4.7.5")

// Lincheck for concurrency testing
testImplementation("org.jetbrains.kotlinx:lincheck:2.24")

// JUnit for testing
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

application {
mainClass = "com.vicitori.app.Main"
}

tasks.register<Jar>("fatJar") {
group = "build"
archiveClassifier.set("all")
archiveBaseName.set("image-filtering")

manifest {
attributes["Main-Class"] = "com.vicitori.app.Main"
}

from(sourceSets.main.get().output)
dependsOn(configurations.runtimeClasspath)
from({
configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }
})
}

// SpotBugs configuration
spotbugs {
ignoreFailures = false
}


60 changes: 60 additions & 0 deletions app/src/main/java/com/vicitori/app/FilteringEngine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.vicitori.app;

import com.vicitori.core.Convolution;
import com.vicitori.core.Filter;
import com.vicitori.core.conv.*;
import com.vicitori.core.filters.FiltersLibrary;
import com.vicitori.io.ImageIO;
import com.vicitori.pipeline.ImagePipeline;

import java.awt.image.BufferedImage;
import java.util.Objects;

public class FilteringEngine {
private final ImageIO io;

public FilteringEngine(ImageIO io) {
this.io = Objects.requireNonNull(io, "ImageIO cannot be null");
}

public String process(String filterName, String convMode) throws ProcessingException {
try {
Filter filter = FiltersLibrary.get(filterName.toLowerCase());
if (filter == null) {
throw new ProcessingException("FilteringEngine: process: Unknown filter: " + filterName + ". Available: " + FiltersLibrary.getNames());
}
Convolution convolution = createConvolution(convMode);
BufferedImage image = io.getImage();
BufferedImage result = convolution.apply(image, filter);
io.writeImage(result);
return io.getOutputPath().toString();
} catch (Exception e) {
throw new ProcessingException("FilteringEngine: process: " + e.getMessage());
}
}

public String process(String filterName, String convMode, int workers) throws ProcessingException {
try {
Filter filter = FiltersLibrary.get(filterName.toLowerCase());
if (filter == null) {
throw new ProcessingException("FilteringEngine: process: with workers: Unknown filter: " + filterName + ". Available: " + FiltersLibrary.getNames());
}
Convolution convolution = createConvolution(convMode);
new ImagePipeline(io.getInputFiles(), io.getOutputDir(), workers, convolution, filter).start();
return io.getOutputDir().toString();
} catch (Exception e) {
throw new ProcessingException("FilteringEngine: process: with workers: " + e.getMessage());
}
}

private Convolution createConvolution(String convMode) {
if (convMode == null) return new SequentialConvolution();
return switch (convMode.toLowerCase()) {
case "row" -> new RowConvolution();
case "column" -> new ColumnConvolution();
case "pixel" -> new PixelConvolution();
case "grid" -> new GridConvolution();
default -> new SequentialConvolution();
};
}
}
Loading