{"id":35519419,"url":"https://github.com/aranadedoros/assetflow","last_synced_at":"2026-01-13T23:01:05.204Z","repository":{"id":321351656,"uuid":"1084771438","full_name":"AranaDeDoros/AssetFLOW","owner":"AranaDeDoros","description":"A  functional-oriented image processing toolkit written in Scala. Designed for web development workflows.","archived":false,"fork":false,"pushed_at":"2026-01-06T23:00:44.000Z","size":2164,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-13T19:43:26.035Z","etag":null,"topics":["image-processing","scala","scala2","scrimage"],"latest_commit_sha":null,"homepage":"https://aranadedoros.github.io/AssetFLOW/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AranaDeDoros.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-28T06:15:18.000Z","updated_at":"2026-01-06T23:00:41.000Z","dependencies_parsed_at":"2025-10-29T07:32:38.055Z","dependency_job_id":"97289d06-f4eb-4071-bbc5-833ccaf05c41","html_url":"https://github.com/AranaDeDoros/AssetFLOW","commit_stats":null,"previous_names":["aranadedoros/web-image-converter","aranadedoros/scala-web-image-toolkit","aranadedoros/assetflow"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AranaDeDoros/AssetFLOW","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AranaDeDoros%2FAssetFLOW","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AranaDeDoros%2FAssetFLOW/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AranaDeDoros%2FAssetFLOW/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AranaDeDoros%2FAssetFLOW/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AranaDeDoros","download_url":"https://codeload.github.com/AranaDeDoros/AssetFLOW/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AranaDeDoros%2FAssetFLOW/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28405148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T21:51:37.118Z","status":"ssl_error","status_checked_at":"2026-01-13T21:45:14.585Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["image-processing","scala","scala2","scrimage"],"created_at":"2026-01-03T23:16:01.410Z","updated_at":"2026-01-13T23:01:05.197Z","avatar_url":"https://github.com/AranaDeDoros.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AssetFLOW\n\u003cp align=\"center\"\u003e\u003cimg height=\"500\" alt=\"assetflow\"\nsrc=\"https://github.com/user-attachments/assets/185054c9-caea-4228-8fe9-d4165e1915ab\" /\u003e\u003c/p\u003e\n\nA lightweight, functional-oriented image processing toolkit written in **Scala**, designed for **web development** workflows.\n\nThis library handles image transformations like conversion to WebP, thumbnail generation, \ncolor manipulation, and OCR preprocessing; using a **functional approach**.\n\n---\n\n## Features\n- [x] **Convert images to WebP**\n- [x] **Generate thumbnails** for desktop and mobile\n- [x] **Create blurred placeholders** for skeletons, templating, etc.\n- [x] **Perform OCR preprocessing** (binarization, tilt, contrast, etc.)\n- [x] **Manipulate and blend colors** functionally\n- [x] **Follow common web image type guidelines**\n---\n\n## Stack\n| Component      | Description                                  |\n|----------------|----------------------------------------------|\n| **Scala 2.13** | Host language.                               |\n| **Scrimage**   | Image manipulation library                   |\n| **Java AWT**   | For color handling and basic graphics        |\n| **ColorThief** | Aids in the palette extraction functionality |\n\n---\n### helpers\n````scala\n  Pipe()\n  .from(\"in\")\n  .to(\"out\")\n  .inspect(\"after load\") { img =\u003e\n    println(img.width, img.height)\n  }\n  .step(TransformationStep(\"grayscale\", OCR.grayscale))\n  .inspect(\"after grayscale\") { img =\u003e\n    println(\"ok\")\n  }\n  .dryRun()\n\nBatch()\n  .from(\"in\")\n  .to(\"out\")\n  .foreach(\"convert\") { (file, out, in) =\u003e\n    ImageTransforms.convertTo(file, out, ImageTransforms.Webp)\n  }.dryRun()\n\n````\n\n### image creation\n````scala\n  //folders setup\nval inputDir = new File(\"input\")\nval outputDir = new File(\"output\")\noutputDir.mkdirs()\n\n//listing images\nval images = ImageTransforms.listImages(inputDir)\nprintln(s\" ${images.size} found ${inputDir.getPath}:\")\nimages.foreach(f =\u003e println(s\"  - ${f.getName}\"))\n\n//converting to webp\nval webpResults = ImageTransforms.convertTo(images, outputDir, Webp)\nwebpResults.foreach {\n  case Right(f) =\u003e println(s\"WebP at: ${f.getName}\")\n  case Left(err) =\u003e println(s\"Error: $err\")\n}\n\n//making thumbnails\nval thumbsDesktop = ImageTransforms.createThumbnail(images, outputDir, Desktop)\nval thumbsMobile = ImageTransforms.createThumbnail(images, outputDir, Mobile)\n\nprintln(\"Thumbnails desktop:\")\nthumbsDesktop.foreach {\n  case Right(f) =\u003e println(s\"  - ${f.getName}\")\n  case Left(err) =\u003e println(s\"  - Error: $err\")\n}\n\nprintln(\"Thumbnails mobile:\")\nthumbsMobile.foreach {\n  case Right(f) =\u003e println(s\"  - ${f.getName}\")\n  case Left(err) =\u003e println(s\"  - Error: $err\")\n}\n\n//now placeholders\nval placeholders = ImageTransforms.generatePlaceholders(\n  number = 3,\n  width = 200,\n  height = 200,\n  fillColor = Some(Color.RED),\n  applyBlur = true,\n  outputDir = outputDir\n)\n\nplaceholders.foreach {\n  case Right(f) =\u003e println(s\"placeholder generated: ${f.getName}\")\n  case Left(err) =\u003e println(s\"Error: $err\")\n}\n\n//test OCR preprocessing\nimages.headOption.foreach { imgFile =\u003e\n  println(\"testing ocr processing...\")\n  val image = ImmutableImage.loader().fromFile(imgFile)\n  val processed = OCR.optimize(\n    image,\n    tilt = 5.0,\n    contrastFactor = 1.3,\n    threshold = 128,\n    doBinarize = true\n  )\n  val (name, ext) = Common.getNameAndExtension(imgFile.getName)\n  val key = Common.timestamp\n  val outPath = new File(outputDir, s\"${name}_$key${ext.getOrElse(\"\")}\").getPath\n  processed.output(WebpWriter.MAX_LOSSLESS_COMPRESSION, new File(outPath))\n  println(s\"OCR processed stored at: $outPath\")\n}\n````\n ### color examples\n```scala\n//create a color\nval redColor = RGBColor(100, 50, 200)\nprintln(s\"initial color: $redColor, hex=${redColor.toHex}\")\n\n//increase channels\nval brighter = redColor.increaseAll(50, 30, -100)\nprintln(s\"adjusted color: $brighter, hex=${brighter.toHex}\")\n\n//mix 'em up\nval blueColor = RGBColor(0, 0, 255)\nval mixed = redColor.mixWith(blueColor, 0.5)\nprintln(s\"50% mix: $mixed, hex=${mixed.toHex}\")\n\n//from hex\nval fromHex = RGBColor.fromHex(\"#ff00cc\")\nprintln(s\"from hex '#ff00cc': $fromHex\")\n\n//random color\nval randomColor = RGBColor.random()\nprintln(s\"random color: $randomColor, hex=${randomColor.toHex}\")\n\nval cmyk = CMYKColor(20, 40, 60, 10)\nval rgb = RGBColor(100, 150, 200)\n\n//increase specific channel by 10 (using currying)\nval brighterMagenta = cmyk.modifyChannel(Magenta)(_ + 10)\nprintln(brighterMagenta)\n\n//using the shortcut increaseChannel\nval brighterRed = rgb.increaseChannel(Red, 20)\nprintln(brighterRed)\n\n//more complex HOF: halve yellow\nval lessYellow = cmyk.modifyChannel(Yellow)(_ / 2)\nprintln(lessYellow)\n\n```\n### output\n```\ninitial color: RGBColor(100,50,200), hex=#6432C8\nadjusted color: RGBColor(150,80,100), hex=#965064\n50% mix: RGBColor(50,25,227), hex=#3219E3\nfrom hex '#ff00cc': RGBColor(255,0,204)\nrandom color: RGBColor(75,123,240), hex=#4B7BF0\n```\n\n### web guidelines examples\n```scala\nWebsiteImageType.summary()\nWebsiteImageType.fromName(\"logo_square\") match {\n  case Some(img) =\u003e println(s\"square logo found guidelines for desktop: ${img.desktop} \" +\n    s\" for mobile: ${img.mobile}, ratio=${img.ratio}\")\n  case None =\u003e println(\"square logo not found\")\n```\n### output\n```\nType                 Desktop (WxH)        Mobile (WxH)         Ratio\n----------------------------------------------------------------------\nbackground           2560x1400            360x640              64:35\nhero                 1280x720             360x200              16:9\nbanner               1200x400             360x120              3:1\nblog                 1200x800             360x240              3:2\nlogo_rectangle       400x100              160x40               4:1\nlogo_square          100x100              60x60                1:1\nfavicon              16x16                16x16                1:1\nsocial_icon          32x32                48x48                1:1\nlightbox             1920x1080            360x640              16:9\nthumbnail            300x300              90x90                1:1\nproduct_thumbnail    300x300              150x150              1:1\n\nsquare logo found guidelines for desktop: 100x100  for mobile: 60x60, ratio=1:1\n```\n---\n# TODO #\n- [x] Create abstractions for creating Pipelines and Batches\n- [x] Add a true Asset pipeline.\n- [ ] Add resume and collection of results to Pipe and Batch.\n- [ ] Extend image extensions support.\n---\n# Docs #\n[AssetFLOW](https://aranadedoros.github.io/AssetFLOW/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faranadedoros%2Fassetflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faranadedoros%2Fassetflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faranadedoros%2Fassetflow/lists"}