{"id":13711122,"url":"https://github.com/slyrus/opticl","last_synced_at":"2025-05-06T20:31:54.280Z","repository":{"id":1376310,"uuid":"1329195","full_name":"slyrus/opticl","owner":"slyrus","description":"An image processing library for Common Lisp","archived":false,"fork":false,"pushed_at":"2022-02-07T06:28:15.000Z","size":3993,"stargazers_count":179,"open_issues_count":7,"forks_count":35,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-08-03T23:23:16.654Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"davidsantiago/clojure-csv","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slyrus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-02-04T19:08:33.000Z","updated_at":"2024-07-28T21:23:04.000Z","dependencies_parsed_at":"2022-07-07T07:03:09.887Z","dependency_job_id":null,"html_url":"https://github.com/slyrus/opticl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slyrus%2Fopticl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slyrus%2Fopticl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slyrus%2Fopticl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slyrus%2Fopticl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slyrus","download_url":"https://codeload.github.com/slyrus/opticl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224528340,"owners_count":17326346,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-08-02T23:01:04.706Z","updated_at":"2024-11-13T21:31:30.857Z","avatar_url":"https://github.com/slyrus.png","language":"Common Lisp","readme":"# opticl: A library for representing and processing images in Common Lisp (CL)\n\nBy Cyrus Harmon \u003cch-lisp@bobobeach.com\u003e, February 2011. See COPYRIGHT\nfile for license details.\n\n# opticl-core\n\nNOTE: If you are using this version (or later) of opticl, you now\nalso need [opticl-core](https://github.com/slyrus/opticl-core).\n\n# Overview\n\nopticl is designed to be a high-performance, but relatively\nlightweight, library for representing, processing, loading, and saving\n2-dimensional pixel-based images. opticl aims to improve upon my first\nattempt at an image processing library -- ch-image, and also borrows\nsome ideas from Matthieu Villeneuve's excellent imago image processing\nlibrary. Representing and processing images provides an excellent\nillustration of the trade-offs between generality, and complexity, on\nthe one hand, and simplicity and efficiency, on the other hand. All\nother things being equal, one generally wants a simple system that is\nboth efficient and general enough to be suitable for use in a variety\nof different contexts -- opticl aims to strike this balance and to be\nboth broadly applicable in different contexts and to provide a core\nset of functionality that is of high-enough performance to be useful\nin time-(and resource-)sensitive operations.\n\n# Installation\n\nThe easiest way to install opticl is to use Zachary Beane's fabulous\nquicklisp library:\n\n    (ql:quickload 'opticl)\n\n# For the Impatient\n\nFor a quick example, let's load an image (of a truck) from a JPEG\nfile, invert the red channel and save the image back out to another\njpeg file:\n\n\n    (defpackage #:impatient (:use #:cl #:opticl))\n    (in-package #:impatient)\n\n    (let ((img (read-jpeg-file \"test/images/truck.jpeg\")))\n      (typecase img\n        (8-bit-rgb-image\n         (locally\n             (declare (type 8-bit-rgb-image img))\n           (with-image-bounds (height width)\n               img\n             (time\n              (loop for i below height\n                 do (loop for j below width \n                       do \n                       (multiple-value-bind (r g b)\n                           (pixel img i j)\n                         (declare (type (unsigned-byte 8) r g b))\n                         (setf (pixel img i j)\n                               (values (- 255 r) g b))))))))))\n      (write-jpeg-file \"test/output/inv-r-truck.jpeg\" img))\n\n\nIf we time the `(loop for i below...)` using the time macro, with SBCL we see\nthe following:\n\n    Evaluation took:\n      0.006 seconds of real time\n      0.005708 seconds of total run time (0.005538 user, 0.000170 system)\n      100.00% CPU\n      11,694,688 processor cycles\n      0 bytes consed\n\nWhich shows that we're able to perform simple arithmetic operations on\neach pixel of the image in 6 milliseconds, and that we don't need to\ncons to do so.\n\n# Image Representation\n\nIn ch-image, images were represented by a set of CLOS classes which,\nin turn, either extended or wrapped classes from the CLEM\nmatrix-processing library. The idea was that CLEM could do the heavy\nlifting and ch-image could take advantage of CLEM's relatively\nefficient routines for storing arrayed sets of 2-dimensional\nnumbers. This worked reasonably well, and allowed for ch-image to have\na great variety of, at least conceptual, image types, such as various\nsizes of RGB and grayscale images, multichannel images, floating point\nimages, binary images, etc..., but this approach had to fundamental\ncosts. First, it required that client programs wishing to use ch-image\nuse CLEM as well -- and CLEM brings along a host of other things that\nmay not be desired by the image-library-using programmer. Second, and\nmore problematic, it relied on CLEM's facilities for accessing image\ndata, or digging deeply into CLEM data structures to get access to the\nunderlying data, which seems to be missing the point.\n\nSo... I've taken a different approach with opticl, which is to largely\neschew CLOS classes and to provide the image data directly as native\nCL arrays. Clearly, some measure of abstraction can be useful to\ninsulate programmers from subsequent changes in the implementation,\nbut this abstraction should be kept to a minimum and should not get in\nthe way of programmers seeking to use the data. Therefore, the\nfundamental data structure of opticl is the CL array, but the API to\ncreate and access the data in these arrays is a set of functions that\nare used to make images and to get and set the data in the\nimages. These functions are implemented as non-generic functions,\nwhich can be inlined (with a sufficiently smart compiler) for\nefficient access to image data. To date, opticl has only been tested\non SBCL, and, conversely, has been designed to exploit the\nperformance-enhancing characteristics of the SBCL compiler, such as\nefficient access to specialized arrays (given proper type\ndeclarations). opticl contains CL types (not classes) and the core\nfunctions for creating and accessing and setting pixel values use\nthese type declarations to enable SBCL to generate relatively\nefficient code for accessing image data.\n\n## Multi-dimensional Arrays\n\nCommon Lisp's multidimensional arrays provide some attractive\nqualities for representing images. At the core, it is desirable to\nhave a representation that lends itself to efficient operations --\nmany languages offer high performance one-dimensional array access,\nand some offer efficient access to multidimensional arrays. However,\nmerely the bytes that comprise the underlying array may not be\nsufficient for one to intelligently use the array. But the bytes that\nmake up the image are only part of the story, the other critical\npieces are the data that describes the bytes in those arrays, the\ndimensions of the image, the number of image channels, etc... In\nch-image I used CLOS classes for this data and for holding a reference\nto the underlying pixels themselves. Fortunately, CL's array type\nitself enables us to store this metadata directly in a\nmultidimensional array. We define a mapping between various image\ntypes and various specialized CL array types, such that, for instance,\nan 8-bit RGB array is represented by the type `(SIMPLE-ARRAY\n(UNSIGNED-BYTE 8) (* * 3))`. Any 3-dimensional simple-array with a\nthird dimension of size 3 and an element-type of `(unsigned-byte 8)`\nwill satisfy the conditions of being an `8-bit-rgb-image`.\n\nThis enables both opticl code and user code to infer the dimensions\nand the kind of pixels represented in a(n appropriate) CL array that\nhappens to be on opticl `image`. This, in turn, allows for both opticl\ncode and user code to use type declarations to enable the compiler to\ngenerate high-performance code for processing images. It is chiefly\nthis facility that distinguishes opticl from other CL image processing\nlibraries such as ch-image and imago.\n\n## Multiple Values\n\nAnother facility afforded by CL, is the notion of multiple values. If\none wants to represent a pixel of an 8-bit RGB image, and to perform\nan operation on the individual color values of this pixel, one is\npresented with a number of alternatives. Without using\nmultiple-values, one can treat the pixel as a single 24-bit unsigned\ninteger, knowing which bits correspond to the red, green and blue\nchannels; one can get the values as a list of three 8-bit integers; or\none can rely on reader/writer functions. Each of these alternatives\nhas some drawbacks.\n\nThe 24-bit unsigned integer approach is relatively clean, but requires\nthat user code unpack the image into it's respective components. Easy\nenough to do, but we just lost two things. First, the image would now\nbe represented as an array of unsigned 24-bit integers -- or in the\ncase of an RGBA image, unsigned 32-bit integers. How would one\ndistinguish this from a 32-bit grayscale image? One would need\nadditional information. Second, one would be relying on either user\ncode or library-provided facilities for unpacking the color\ninformation. It is my belief that the compiler is going to do at least\nas good of a job as user code in pulling those values out of an\nadditional array dimension than user or library code would. On the\nother hand, using a list or reader/writer functions would likely\ninvolve heap-allocation of data structures to store this information.\n\nCL offers a facility that has the potential to alleviate these issues,\nwhich is `multiple-values`. This allows us to return multiple (perhaps\nstack-allocated) values from a function and for us to to efficiently\nupdate the values in multiple places using `setf`. Furthermore, it\nallows for a unified treatment of grayscale and RGB pixels as a\ngrayscale pixel is just a single value, while an RGB pixel is\nrepresented by multiple values, as opposed to treating grayscale\nvalues as an integer and RGB values as a list of integers. All of this\nwould just be theoretical navel-gazing if the implementations didn't\ntake advantage of the features of multiple values to provide efficient\ncompiled implementations of code that uses these\nfeatures. Fortunately, SBCL's implementation of multiple-values allows\nus to define (possibly inline) reader and writer functions that can\naccess the pixel and color-value data efficiently and without\nallocating additional memory on the heap (consing).\n\nThe trade-off in this approach is that doing so requires that we know\nthe kind of image with which are dealing, at least if we want to do so\nefficiently. Fortunately, CL's type system gets us most of the way\nthere. I say most of the way there, as there is one limitation in\nstandard, which we will see in a moment. In the example above you'll\nnotice a line which reads:\n\n    (declare (type 8-bit-rgb-image img))\n\nThis declaration tells the compiler that the variable image is of the\ntype 8-bit-rgb-image and the compiler is able to optimize the code\neffectively. The problem is that this is great for things inside the\ncompiler, the compiler sees the declaration and can act accordingly,\nbut only the compiler can do so. In CL, these declarations are opaque\nto the user/library programmer. This limitation wasn't lost on the\nearly CL implementors, but facilities for inspecting declarations\ndidn't make it into the CL spec, but rather, eventually, found there\nway into the less-widely implemented Common Lisp the Lanuage, 2nd\nEdition (CLtL2) book by Guy Steele. SBCL has a contrib library called\nsb-cltl2 that provides the key facility we need,\n`cltl2:variable-information`. We can use that function in code called\nfrom our define-setf-expander, as shown below in get-image-dimensions,\nto see if there is a declaration in effect:\n\n    (defun get-image-dimensions (image-var env)\n      (multiple-value-bind (binding-type localp declarations)\n          (cltl2:variable-information image-var env)\n        (declare (ignore binding-type localp))\n        (let ((type-decl (find 'type declarations :key #'car)))\n          (and type-decl\n               (listp type-decl)\n               (= (length type-decl) 4)\n               (fourth type-decl)))))\n\nThis allows us to glean information from the information provided to\nthe compiler that enables opticl to efficiently operate on its images,\nwhen given appropriate declarations, and still work, albeit less\nefficiently, in the absence of the appropriate type declarations.\n\n(Note: I still need to look into the availability of the CLtL2\nfunctionality on other CL implementations.)\n\nIt is the representation of image data as native CL arrays and the\nefficient performance of these reader and writer functions that offer\nthe hope that opticl can serve as a general purpose image processing\nlibrary suitable for use by a wide variety of CL programs.\n\n# Supported File Formats\n\n* PNG\n* JPEG\n* TIFF\n* PBM\n* PNM\n* GIF\n\n# Dependencies\n\nWhile opticl is designed to have minimal dependencies, I have decided\nthat it is better to use existing libraries, where possible,\nespecially for file I/O of various formats. In ch-image, I tried to\nmake the file I/O sections optional dependencies, but this proved\nmerely to sow confusion into the minds of the user. With the advent of\nquicklisp, dependencies on libraries that are in quicklisp are much\nless painful (for the quicklisp user anyway) than they used to be.\n\n* alexandria\n* retrospectiff (new version -- as of??)\n* com.gigamonkeys.binary-data (also known as monkeylib-binary-data)\n* ieee-floats\n* zpng\n* salza2\n* png-read\n* iterate\n* chipz\n* babel\n* cl-jpeg\n* skippy\n\nopticl and all of its dependencies should be automatically installed\nby:\n\n    (ql:quickload 'opticl)\n\n# opticl-core\n\nThe new [opticl-core](https://github.com/slyrus/opticl-core) package\nnow contains the core machinery for representing images and accessing\nand setting pixel data. It is now required for opticl.\n\n# opticl-more-test and opticl-examples\n\nIn the interest of keeping the core opticl library small, I've split\noff some test code in into\n[opticl-more-test](https://github.com/slyrus/opticl-more-test) and\nmore expository example code into\n[opticl-examples](https://github.com/slyrus/opticl-examples).\n\n# Examples\n\nSome examples of using opticl code can be found here:\n\n[https://github.com/slyrus/opticl-examples](https://github.com/slyrus/opticl-examples)\n\n# Contributors\n\nThanks to Ivan Chernetsky for contributing code thresholding grayscale images\n","funding_links":[],"categories":["Common Lisp","Miscellaneous ##"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslyrus%2Fopticl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslyrus%2Fopticl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslyrus%2Fopticl/lists"}