Skip to content

Rewrite the interface for users #11

@th0rex

Description

@th0rex

So this is going to be much text...

We should really try to rewrite the API in a way that makes it easier to use and more error safe.

Some things that should be kept in mind:

  1. Prefer compile time errors to runtime errors.
  2. Keep runtime overhead low.

Status:

  • Implemented 1)
  • Implemented 2)
  • Implemented 3)
  • Implemented 4)
  • Implemented 5)

So the basic idea I had was the following:

    1. We make a struct Pixel or name it Point. It should just contain the x and y coordinates of a pixel.
    • Why do we even need this struct ? If we have a function that takes something like the following as Parameters: void f(unsinged x, unsigned y, unsigned size) it's easy to pass the parameters in the wrong order. If we also define a Size or Bounds struct that should not happen anymore and we'll get compile time errors instead.
    • Priority: Low
    • Things to still consider: Whether we let a Pixel take an Color template argument, or whether a Color inherits from a Pixel.
    1. Then we define a Color struct. Currently we need RgbColor and GrayColor.
    • Why do we even need this ? To abstract an Image over that. An Image should not have much overhead and thus we shouldn't always store the RGB value of every Pixel, if it's not needed.
    • Priority: High
    • Things to still consider: See above.
    1. After that we need to define an Image class. An Image should provide methods for creating it, converting it to an Image of another color type, cloning it and iterating/changing Pixels. An Image should take a template parameter that specifies the Color that is used.
    • Why do we even need this ? To have a nice and clean API for images of any color type.
    • Priority: High
    • Things to still consider: None ? Maybe it would be better to define this as a Concept, but they won't be in C++ in the near future.
    1. Then we define what an "Algorithm" is for an Image and what's required to implement one.
      I would suggest, that an Algorithm on it's most basic level is just something that needs an Image and maybe modifies it. An Algorithm can also have state (i.e. input parameters) that changes the way it behaves. We'll see later why I call it "state". So if we take ImageFramework::getRGBVariance for an example. The state of that would be int& rVar; int& gVar; int& bVar;. The input would be a const Image& (as it's not actually modified).
    • Why do we even need this ? We need a uniform way of running algorithms. If we would just define them as functions that take parameters directly we wouldn't be able to do what is done in the next step.
    • Priority: High
    • Things to consider: We could make each algorithm define whether they need to modify an Image or not, to give better diagnostics if an const Image& is passed in, but the algorithm would need to mutate it. Important: We could make an algorithm return false; on an error, so that we can cancel an chain of algorithms early if an error occurrs and we won't need exceptions for that.

So our "Algorithm" would look something like this:

namespace detail {
struct RGBVariance{
     int& rOut;
     int& gOut;
     int& bOut;
     // or RgbColor& colorOut

     // This COULD contain a function like
     static RGBVariance createVariance(...)
     // that we can "import" into the outer namespace, so the user doesn't get access
     // to this whole class, if he just does "using namespace image::algorithms;".
     // He would then rather get access to all the creation functions like createVariance and so on.

     // Constructor that sets the input/output parameters needed.
     RGBVariance(...)

     // Actual algorithm
     template<typename TColor>
     void operator()(const Image<TColor>& img) {
         // This function would cause an compile time error by doing some magic only if it's called.
         // Because we don't want to run "**RGB**Variance" on an image of ANY color.
     }

     template<>
     void operator()(const Image<RgbColor>& rgbImg) {
         // This function is only called for RgbImages. So it could compute the variance and set
         // the output values.
     }

     // We could be more clever, and define this algorithm just as "Variance" and then also implement
     // it for the GrayScaleColor. Then the output parameter would need to be pointers, as they can be
     // nullptr, but that won't change much. And we would define a second constructor that just takes a
     // GrayColor& out.
};
}
    1. And the final step to make the algorithms nicely usable is to define two functions that allow chaining of algorithms.
    • Why do we even need this ? To make user code look nice. User code would look something like this.
    • Priority: High
    • Things to consider: ???

Implementation:

template<typename... TAlgorithms>
auto createAlgorithm(TAlgorithms&&... algorithmsToRun){
    // some magic here I won't write out while still in the planning face, that makes a chain of lambdas
    // and returns a lambda that takes a single "Image&" or "const Image&" argument.
    // algorithmsToRun would need to be things for wich we can call algorithm::operator()(img);
}

template<template<typename> typename TImage, typename... TAlgorithms>
bool /*<- if we decide to return bools, else void*/ transformImage(TImage& img, TAlgorithms&&... algorithmsToRun) {
     return createAlgorithm(std::forward<TAlgorithms>(algorithmsToRun)...)(img);
}

User code:

using namespace image::algorithms;
using namespace image::loaders;

void f() {
    auto image = loadImageFromFile<RgbColor>("myImage.png");
    auto rOut = 0;
    auto gOut = 0;
    auto bOut = 0;
    // or auto colorOut = RgbColor{};
    auto success = transformImage(image,
             createConvolutionFilter(filter, ...),
             invertImage(),
             getRGBVariance(rOut, gOut, bOut));
}

Please comment on this an post any ideas/suggestions/improvements/questions.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions