[libcamera-devel,RFC,v1,2/3] pipeline: isp: The software ISP class
diff mbox series

Message ID tencent_FF0DB70FBE0E0D89696050DFCEEEA04DAA08@qq.com
State Superseded
Headers show
Series
  • pipeline: isp: The software ISP Module
Related show

Commit Message

Siyuan Fan Aug. 4, 2021, 7:33 a.m. UTC
From: Fan Siyuan <siyuan.fan@foxmail.com>

Add the software ISP class only CPU-based. So far the basic alogrithm
includes black level correct, bilinear demosaic interpolation, auto 
white balance(gray world), tone mapping(auto contrast), gamma correct.
bilateral filter, 10bit to 8bit compression.

Signed-off-by: Fan Siyuan <siyuan.fan@foxmail.com>

---
 src/libcamera/pipeline/isp/isp_processing.cpp | 593 ++++++++++++++++++
 src/libcamera/pipeline/isp/isp_processing.h   |  62 ++
 2 files changed, 655 insertions(+)
 create mode 100644 src/libcamera/pipeline/isp/isp_processing.cpp
 create mode 100644 src/libcamera/pipeline/isp/isp_processing.h

Comments

Paul Elder Aug. 5, 2021, 10:08 a.m. UTC | #1
Hi Siyuan,

On Wed, Aug 04, 2021 at 08:33:46AM +0100, Siyuan Fan wrote:
> From: Fan Siyuan <siyuan.fan@foxmail.com>
> 
> Add the software ISP class only CPU-based. So far the basic alogrithm
> includes black level correct, bilinear demosaic interpolation, auto 
> white balance(gray world), tone mapping(auto contrast), gamma correct.
> bilateral filter, 10bit to 8bit compression.
> 
> Signed-off-by: Fan Siyuan <siyuan.fan@foxmail.com>
> 
> ---
>  src/libcamera/pipeline/isp/isp_processing.cpp | 593 ++++++++++++++++++
>  src/libcamera/pipeline/isp/isp_processing.h   |  62 ++
>  2 files changed, 655 insertions(+)
>  create mode 100644 src/libcamera/pipeline/isp/isp_processing.cpp
>  create mode 100644 src/libcamera/pipeline/isp/isp_processing.h
> 
> diff --git a/src/libcamera/pipeline/isp/isp_processing.cpp b/src/libcamera/pipeline/isp/isp_processing.cpp
> new file mode 100644
> index 00000000..94e8081d
> --- /dev/null
> +++ b/src/libcamera/pipeline/isp/isp_processing.cpp
> @@ -0,0 +1,593 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Siyuan Fan <siyuan.fan@foxmail.com>
> + *
> + * isp_processing.cpp - The software ISP class
> + */
> +
> +#include "isp_processing.h"
> +
> +#include <math.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/mman.h>
> +#include <unistd.h>
> +
> +#include <libcamera/request.h>
> +
> +#include "libcamera/base/log.h"
> +
> +namespace libcamera{
> +
> +LOG_DECLARE_CATEGORY(ISP)
> +
> +void ISP::autoContrast(uint16_t *data, float lowCut, float highCut, int width, int height)
> +{
> +        int blue, gr, gb, red;
> +        int histBlue[1024] = {0}, histGb[1024] = {0}, histGr[1024] = {0}, histRed[1024] = {0};

From what I see from the caller of this, data is already demosaiced, so
it only includes one color. How are you going to get a histogram of the
other colors?

> +
> +        int index;
> +        for (int i = 0; i < height; i++) {
> +                index = i * width;

I see you using this idiom a lot. I think it's better to assign it under
the j loop...

> +                for (int j = 0; j < width; j++) {

... here, int index = i * width + j;

Same everywhere else you use this pattern.

> +                        if (i % 2 == 0 && j % 2 == 0) {

It would be nice if you could figure out some way to allow this to work
with any raw format, but I guess you don't have to worry about it now,
but make sure to add a configure() function to your ISP so that you can
reject formats that you don't support.

> +                                blue = data[index];
> +                                histBlue[blue]++;
> +                        }
> +                        else if ((i % 2 == 0 && j % 2 == 1)) {

else and else if should both go on the same line as }

> +                                gb = data[index];
> +                                histGb[gb]++;
> +                        }
> +                        else if ((i % 2 == 1 && j % 2 == 0)) {
> +                                gr = data[index];
> +                                histGr[gr]++;
> +                        }
> +                        else {
> +                                red = data[index];
> +                                histRed[red]++;
> +                        }
> +                        index++;
> +                }    
> +        }

I think it would be useful to move this calculation of the histogram to
another function, so if other algorithms in the future want to use the
histogram they can (plus you can expose it as a function to the pipeline
handlers for IPAs!).

> +
> +        int pixelAmount = width * height;
> +        int sum = 0;
> +        int minBlue;
> +        for (int i = 0; i < 1024; i++){
> +                sum = sum + histBlue[i];
> +                if (sum >= pixelAmount * lowCut * 0.01) {
> +                        minBlue = i;
> +                        break;
> +                }
> +        }
> +
> +        sum = 0;
> +        int maxBlue;
> +        for (int i = 1023; i >= 0; i--){
> +                sum = sum + histBlue[i];
> +                if (sum >= pixelAmount * highCut * 0.01) {
> +                        maxBlue = i;
> +                        break;
> +                }
> +        }

I see these two blocks are reused over and over again below. These
should be moved to other functions.

> +
> +        sum = 0;
> +        int minGb;
> +        for (int i = 0; i < 1024; i++){
> +                sum = sum + histGb[i];
> +                if (sum >= pixelAmount * lowCut * 0.01) {
> +                        minGb = i;
> +                        break;
> +                }
> +        }
> +
> +        sum = 0;
> +        int maxGb;
> +        for (int i = 1023; i >= 0; i--){
> +                sum = sum + histGb[i];
> +                if (sum >= pixelAmount * highCut * 0.01) {
> +                        maxGb = i;
> +                        break;
> +                }
> +        }
> +
> +        sum = 0;
> +        int minGr;
> +        for (int i = 0; i < 1024; i++){
> +                sum = sum + histGr[i];
> +                if (sum >= pixelAmount * lowCut * 0.01) {
> +                        minGr = i;
> +                        break;
> +                }
> +        }
> +
> +        sum = 0;
> +        int maxGr;
> +        for (int i = 1023; i >= 0; i--){
> +                sum = sum + histGr[i];
> +                if (sum >= pixelAmount * highCut * 0.01) {
> +                        maxGr = i;
> +                        break;
> +                }
> +        }
> +
> +        sum = 0;
> +        int minRed;
> +        for (int i = 0; i < 1024; i++){
> +                sum = sum + histRed[i];
> +                if (sum >= pixelAmount * lowCut * 0.01) {
> +                        minRed = i;
> +                        break;
> +                }
> +        }
> +
> +        sum = 0;
> +        int maxRed;
> +        for (int i = 1023; i >= 0; i--){
> +                sum = sum + histRed[i];
> +                if (sum >= pixelAmount * highCut * 0.01) {
> +                        maxRed = i;
> +                        break;
> +                }
> +        }
> +
> +        int blueMap[1024];
> +        float norb = 1.0 / (maxBlue - minBlue);
> +        for (int i = 0; i < 1024; i++) {
> +                if (i < minBlue) {
> +                        blueMap[i] = 0;
> +                }
> +                else if (i > maxBlue) {
> +                        blueMap[i] = 1023;
> +                }
> +                else {
> +                        blueMap[i] = (i - minBlue) * norb * 1023;
> +                }
> +                if (blueMap[i] > 1023) blueMap[i] = 1023;
> +        }

Same for these; these should be moved to a function.

> +
> +        int gbMap[1024];
> +        float norgb = 1.0 / (maxGb - minGb);
> +        for (int i = 0; i < 1024; i++) {
> +                if (i < minGb) {
> +                        gbMap[i] = 0;
> +                }
> +                else if (i > maxGb) {
> +                        gbMap[i] = 1023;
> +                }
> +                else {
> +                        gbMap[i] = (i - minGb) * norgb * 1023;
> +                }
> +                if (gbMap[i] > 1023) gbMap[i] = 1023;
> +        }
> +
> +        int grMap[1024];
> +        float norgr = 1.0 / (maxGr - minGr);
> +        for (int i = 0; i < 1024; i++) {
> +                if (i < minGr) {
> +                        grMap[i] = 0;
> +                }
> +                else if (i > maxGr) {
> +                        grMap[i] = 1023;
> +                }
> +                else {
> +                        grMap[i] = (i - minGr) * norgr * 1023;
> +                }
> +                if (grMap[i] > 1023) grMap[i] = 1023;
> +        }
> +
> +        int redMap[1024];
> +        float norr = 1.0 / (maxRed - minRed);
> +        for (int i = 0; i < 1024; i++) {
> +                if (i < minRed) {
> +                        redMap[i] = 0;
> +                }
> +                else if (i > maxRed) {
> +                        redMap[i] = 1023;
> +                }
> +                else{
> +                        redMap[i] = (i - minRed) * norr * 1023;
> +                }
> +                if (redMap[i] > 1023) redMap[i] = 1023;
> +        }
> +
> +        for (int i = 0;i < height; i++) {
> +                for (int j = 0; j < width; j++){
> +                        index = i * width;
> +                        if (i % 2 == 0 && j % 2 == 0) {
> +                                data[index] = blueMap[data[index]];
> +                        }    
> +                        else if (i % 2 == 0 && j % 2 == 1) {
> +                                data[index] = gbMap[data[index]];
> +                        }    
> +                        else if (i % 2 == 1 && j % 2 == 0) {
> +                                data[index] = grMap[data[index]];
> +                        }    
> +                        else {
> +                                data[index] = redMap[data[index]];
> +                        }
> +                        index++;
> +                }
> +        }
> +}
> +
> +void ISP::blackLevelCorrect(uint16_t *data, uint16_t offset, int width, int height)

I see you hardcode offset to 16 in the caller. Where does that number
come from?

> +{
> +        int len = width * height;
> +        for(int i = 0; i < len; i++) {
> +                if (data[i] < offset){
> +                        data[i] = 0;
> +                }
> +                else {
> +                        data[i] -= offset;
> +                }
> +        }
> +}
> +
> +void ISP::readChannels(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
> +                       int width, int height)

I think you don't need this function, and you can just add another
clause in your demosaic functions...

> +{
> +        int index;
> +        for (int i = 0; i < height; i++) {
> +                index = i * width;
> +                for (int j = 0; j < width; j++) {
> +                        if (i % 2 == 0 && j % 2 == 0) {
> +                                B[index] = data[index];
> +                        }
> +                        else if ((i % 2 == 0 && j % 2 == 1) || (i % 2 == 1 && j % 2 == 0)){
> +                                G[index] = data[index];
> +                        }
> +                        else {
> +                                R[index] = data[index];
> +                        }
> +                        index++;
> +                }
> +        }
> +}
> +
> +void ISP::firstPixelInsert(uint16_t *src, uint16_t *dst, int width, int height)
> +{
> +        int index;
> +        for (int i = 0; i < height; i++) {
> +                index = i * width;
> +                for (int j = 0; j < width; j++){

I think some comments here would help, to describe each clause. After I
built the mental image it was easy to follow but before that it was
hard.

> +                        if (i % 2 == 0 && j % 2 == 1) {
> +                                if (j == (width - 1)) {
> +                                        dst[index] = src[index - 1];
> +                                }
> +                                else {
> +                                        dst[index] = (src[index - 1] +
> +                                                src[index + 1]) >> 1;
> +                                }
> +                        }
> +
> +                        if (i % 2 == 1 && j % 2 == 0) {
> +                                if(i == height - 1) {
> +                                        dst[index] = src[index - width];
> +                                }
> +                                else {
> +                                        dst[index] = (src[index - width]+
> +                                                src[index + width]) >> 1;
> +                                }
> +                        }
> +    
> +                        if (i % 2 == 1 && j % 2 == 1) {
> +                                if (j < width - 1 && i < height - 1) {
> +                                        dst[index] = (src[index - width - 1] +
> +                                                src[index - width + 1] +
> +                                                src[index + width - 1] +
> +                                                src[index + width + 1]) >> 2;
> +                                }
> +                                else if (i == height - 1 && j < width - 1) {
> +                                        dst[index] = (src[index - width - 1] +
> +                                                src[index - width + 1]) >> 1;
> +                                }
> +                                else if (i < height - 1 && j == width - 1) {
> +                                        dst[index] = (src[index - width - 1] +
> +                                                src[index + width - 1]) >> 1;
> +                                }
> +                                else {
> +                                        dst[index] = src[index - width - 1];
> +                                }          
> +                        }

...like here:

else
	dst[index] = src[index];

> +                        index++;
> +                }
> +        }
> +}
> +
> +void ISP::twoPixelInsert(uint16_t *src, uint16_t *dst, int width, int height)
> +{
> +        int index;
> +        for (int i = 0; i < height; i++) {
> +                index = i * width;
> +                for (int j = 0; j < width; j++) {
> +                        if (i == 0 && j == 0) {
> +                                dst[index] = (src[index + width] +
> +                                        src[index + 1]) >> 1;
> +                        }
> +                        else if (i == 0 && j > 0 && j % 2 == 0) {
> +                                dst[index] = (src[index - 1] +
> +                                        src[index + width] +
> +                                        src[index + 1]) / 3;
> +                        }
> +                        else if (i > 0 && j == 0 && i % 2 == 0) {
> +                                dst[index] = (src[index - width] +
> +                                        src[index + 1] +
> +                                        src[index + width]) / 3;
> +                        }
> +                        else if (i == (height - 1) && j < (width - 1) && j % 2 == 1) {
> +                                dst[index] = (src[index - 1] +
> +                                        src[index - width] +
> +                                        src[index + 1]) / 3;
> +                        }
> +                        else if (i < (height - 1) && j == (width - 1) && i % 2 == 1) {
> +                                dst[index] = (src[index - width] +
> +                                        src[index - 1] +
> +                                        src[index + width]) / 3;
> +                        }
> +                        else if (i == (height - 1) && j == (width - 1)) {
> +                                dst[index] = (src[index - width] +
> +                                        src[index - 1]) >> 1;
> +                        }
> +                        else if ((i % 2 == 0 && j % 2 == 0) || (i % 2 == 1 && j % 2 == 1)) {
> +                                dst[index] = (src[index - 1] +
> +                                        src[index + 1] +
> +                                        src[index - width] +
> +                                        src[index + width]) / 4;
> +                        }
> +                        index++;
> +                }
> +        }
> +}
> +
> +void ISP::lastPixelInsert(uint16_t *src, uint16_t *dst, int width, int height)
> +{
> +        int index;
> +        for (int i = 0; i < height; i++) {
> +                index = i * width;
> +                for (int j = 0; j < width; j++){
> +                        if (i % 2 == 1 && j % 2 == 0) {
> +                                if (j == 0) {
> +                                        dst[index] = src[index + 1];
> +                                }
> +                                else {
> +                                        dst[index] = (src[index - 1] +
> +                                                src[index + 1]) >> 1;
> +                                }
> +                        }
> +
> +                        if (i % 2 == 0 && j % 2 == 1) {
> +                                if(i == 0) {
> +                                        dst[index] = src[index + width];
> +                                }
> +                                else {
> +                                        dst[index] = (src[index - width]+
> +                                                src[index + width]) >> 1;
> +                                }
> +                        }
> +
> +                        if (i % 2 == 0 && j % 2 == 0) {
> +                                if (i > 0 && j > 0) {
> +                                        dst[index] = (src[index - width - 1] +
> +                                                src[index - width + 1] +
> +                                                src[index + width - 1] +
> +                                                src[index + width + 1]) >> 2;
> +                                }
> +                                else if (i == 0 && j > 0) {
> +                                        dst[index] = (src[index + width - 1] +
> +                                                src[index + width + 1]) >> 1;
> +                                }
> +                                else if (i > 0 && j == 0) {
> +                                        dst[index] = (src[index - width + 1] +
> +                                                src[index + width + 1]) >> 1;
> +                                }
> +                                else {
> +                                        dst[index] = src[index + width + 1];
> +                                }
> +                        }
> +                        index++;
> +                }
> +        }
> +}
> +
> +void ISP::demosaic(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
> +                   int width, int height)
> +{
> +        firstPixelInsert(data, B, width, height);
> +        twoPixelInsert(data, G, width, height);
> +        lastPixelInsert(data, R, width, height);

I think these functions could use better names, but I can't think of
anything better :S

> +}
> +
> +void ISP::autoWhiteBalance(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height)
> +{
> +        float aveB = 0, aveG = 0, aveR = 0;
> +        float Kb, Kg, Kr;
> +
> +        for (int i = 0; i < width * height; i++) {
> +                aveB += 1.0 * B[i];
> +                aveG += 1.0 * G[i];
> +                aveR += 1.0 * R[i];
> +        }
> +
> +        aveB *= (1.0 / (width * height));
> +        aveG *= (1.0 / (width * height));
> +        aveR *= (1.0 / (width * height));
> +
> +        Kr = (aveB + aveG + aveR) / aveR * (1.0 / 3.0);
> +        Kg = (aveB + aveG + aveR) / aveG * (1.0 / 3.0);
> +        Kb = (aveB + aveG + aveR) / aveB * (1.0 / 3.0);

I think calculating these could be moved to a separate function too,
just like the histogram.

> +
> +        for (int i = 0; i < width * height; i++) {
> +                B[i] = B[i] * Kb;
> +                G[i] = G[i] * Kg;
> +                R[i] = R[i] * Kr;
> +
> +                if (R[i] > 1023) R[i] = 1023;
> +                if (G[i] > 1023) G[i] = 1023;
> +                if (R[i] > 1023) B[i] = 1023;
> +        }
> +}
> +
> +void ISP::gammaCorrect(uint16_t *R, uint16_t *G, uint16_t *B, float val,
> +                       int width, int height)
> +{
> +        float nor = 1.0 / 1023.0;
> +        float gamma = 1.0 / val;
> +        for (int i = 0; i < width * height; i++) {
> +                R[i] = pow(R[i] * nor, gamma) * 1023;
> +                G[i] = pow(G[i] * nor, gamma) * 1023;
> +                B[i] = pow(B[i] * nor, gamma) * 1023;
> +
> +                if (R[i] > 1023) R[i] = 1023;
> +                if (G[i] > 1023) G[i] = 1023;
> +                if (B[i] > 1023) B[i] = 1023;
> +        }
> +}
> +
> +void ISP::compress_10bit_to_8bit (uint16_t *src, uint8_t *dst, int width, int height)
> +{
> +        for (int i = 0; i < width * height; i++) {
> +                dst[i] = src[i] >> 2 & 0xff;
> +        }
> +}
> +
> +float ISP::distance(int x, int y, int i, int j)
> +{
> +    return float(sqrt(pow(x - i, 2) + pow(y - j, 2)));
> +}
> +
> +double ISP::gaussian(float x, double sigma)
> +{
> +    return exp(-(pow(x, 2)) / (2 * pow(sigma, 2))) / (2 * 3.1415926 * pow(sigma, 2));
> +}
> +
> +void ISP::bilateralFilter(uint16_t *R, uint16_t *G, uint16_t *B,
> +                          int diameter, double sigmaI,
> +                          double sigmaS, int width, int height)
> +{
> +        for (int i = 2; i < height - 2; i++) {
> +                for (int j = 2; j < width - 2; j++) {
> +                        double iFiltered = 0;
> +                        double wp = 0;
> +                        int neighbor_x = 0;
> +                        int neighbor_y = 0;
> +                        int half = diameter / 2;
> +
> +                        for (int k = 0; k < diameter; k++) {
> +                                for (int l = 0; l < diameter; l++) {
> +                                        neighbor_x = i - (half - k);
> +                                        neighbor_y = j - (half - l);
> +                                        double gi = gaussian(R[neighbor_x * width + neighbor_y] - R[i * width +j], sigmaI);
> +                                        double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS);
> +                                        double w = gi * gs;
> +                                        iFiltered = iFiltered + R[neighbor_x * width + neighbor_y] * w;
> +                                        wp = wp + w;
> +                                }    
> +                        }
> +
> +                        iFiltered = iFiltered / wp;
> +                        R[i * width + j] = iFiltered;
> +                }
> +        }
> +
> +        for (int i = 2; i < height - 2; i++) {
> +                for (int j = 2; j < width - 2; j++) {
> +                        double iFiltered = 0;
> +                        double wp = 0;
> +                        int neighbor_x = 0;
> +                        int neighbor_y = 0;
> +                        int half = diameter / 2;
> +
> +                        for (int k = 0; k < diameter; k++) {
> +                                for (int l = 0; l < diameter; l++) {
> +                                        neighbor_x = i - (half - k);
> +                                        neighbor_y = j - (half - l);
> +                                        double gi = gaussian(G[neighbor_x * width + neighbor_y] - G[i * width +j], sigmaI);
> +                                        double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS);
> +                                        double w = gi * gs;
> +                                        iFiltered = iFiltered + G[neighbor_x * width + neighbor_y] * w;
> +                                        wp = wp + w;
> +                                }    
> +                        }
> +
> +                        iFiltered = iFiltered / wp;
> +                        G[i * width + j] = iFiltered;
> +                }
> +        }
> +
> +        for (int i = 2; i < height - 2; i++) {
> +                for (int j = 2; j < width - 2; j++) {
> +                        double iFiltered = 0;
> +                        double wp = 0;
> +                        int neighbor_x = 0;
> +                        int neighbor_y = 0;
> +                        int half = diameter / 2;
> +
> +                        for (int k = 0; k < diameter; k++) {
> +                                for (int l = 0; l < diameter; l++) {
> +                                        neighbor_x = i - (half - k);
> +                                        neighbor_y = j - (half - l);
> +                                        double gi = gaussian(B[neighbor_x * width + neighbor_y] - B[i * width +j], sigmaI);
> +                                        double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS);
> +                                        double w = gi * gs;
> +                                        iFiltered = iFiltered + B[neighbor_x * width + neighbor_y] * w;
> +                                        wp = wp + w;
> +                                }    
> +                        }
> +
> +                        iFiltered = iFiltered / wp;
> +                        B[i * width + j] = iFiltered;
> +                }
> +        }
> +}
> +
> +void ISP::processing(FrameBuffer *srcBuffer, FrameBuffer *dstBuffer, int width, int height)
> +{
> +        uint8_t *rgb_buf;
> +        uint16_t *rawData;
> +        uint16_t *rgbData = new std::uint16_t[width * height * 3];

Instead of using this as a scratch buffer, you could just do the
dstBuffer mmap here already, and then use that directly.

> +
> +        uint16_t *rData = rgbData;
> +        uint16_t *gData = rData + width * height;
> +        uint16_t *bData = gData + width * height;
> +        memset(rgbData, 0x0, width * height * 3);
> +
> +        const FrameBuffer::Plane &plane =  srcBuffer->planes()[0]; 
> +        rawData = (uint16_t *)mmap(NULL, plane.length, PROT_READ|PROT_WRITE, MAP_SHARED, plane.fd.fd(), 0);
> +        if (rawData == MAP_FAILED) {
> +            LOG(ISP, Error) << "Read raw data failed";
> +            ispCompleted.emit(dstBuffer);
> +        }
> +
> +        blackLevelCorrect(rawData, 16, width, height);
> +        readChannels(rawData, rData, gData, bData, width, height);
> +        demosaic(rawData, rData, gData, bData, width, height);
> +        autoWhiteBalance(rData, gData, bData, width, height);
> +        autoContrast(rData, 0.01, 0.01, width, height);
> +        autoContrast(gData, 0.01, 0.01, width, height);
> +        autoContrast(bData, 0.01, 0.01, width, height);
> +        gammaCorrect(rData, gData, bData, 2.2, width, height);
> +        //bilateralFilter(rData, gData, bData, 5, 24.0, 32.0);

Why isn't this used?

> +    
> +        const FrameBuffer::Plane &rgbPlane = dstBuffer->planes()[0];
> +        rgb_buf = (uint8_t *)mmap(NULL, rgbPlane.length, PROT_READ|PROT_WRITE, MAP_SHARED, rgbPlane.fd.fd(), 0);
> +        if (rgb_buf == MAP_FAILED) {
> +                LOG(ISP, Error) << "Read rgb data failed";
> +                ispCompleted.emit(dstBuffer);
> +        }
> +
> +        uint8_t *rData_8 = rgb_buf;
> +        uint8_t *gData_8 = rData_8 + width * height;
> +        uint8_t *bData_8 = gData_8 + width * height;
> +
> +        compress_10bit_to_8bit(rData, rData_8, width, height);
> +        compress_10bit_to_8bit(gData, gData_8, width, height);
> +        compress_10bit_to_8bit(bData, bData_8, width, height);

This isn't a valid format, you have three separate planes of R, G, and
B. iirc you were using RGB888? In which case you need to reorder your
color bytes [1] (libcamera RGB888 is V4L2 V4L2_PIX_FMT_BGR24).

[1] https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-rgb.html


Paul

> +
> +        delete[] rgbData;
> +
> +        ispCompleted.emit(dstBuffer);
> +}
> +
> +} /* namespace libcamera */
> +
> diff --git a/src/libcamera/pipeline/isp/isp_processing.h b/src/libcamera/pipeline/isp/isp_processing.h
> new file mode 100644
> index 00000000..b80b4218
> --- /dev/null
> +++ b/src/libcamera/pipeline/isp/isp_processing.h
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Siyuan Fan <siyuan.fan@foxmail.com>
> + *
> + * isp_processing.h - The software ISP class
> + */
> +#ifndef __LIBCAMERA_PIPELINE_ISP_PROCESSING_H__
> +#define __LIBCAMERA_PIPELINE_ISP_PROCESSING_H__
> +
> +#include <libcamera/framebuffer.h>
> +
> +#include "libcamera/base/signal.h"
> +#include "libcamera/base/object.h"
> +
> +namespace libcamera{
> +
> +using std::uint16_t;
> +using std::uint8_t;
> +
> +class ISP : public Object
> +{
> +public:
> +        void autoContrast(uint16_t *data, float lowCut, float highCut, int width, int height);
> +
> +        void blackLevelCorrect(uint16_t *data, uint16_t offset, int width, int height);
> +
> +        void readChannels(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
> +                          int width, int height);
> +
> +        void firstPixelInsert(uint16_t *src, uint16_t *dst, int width, int height);
> +
> +        void twoPixelInsert(uint16_t *src, uint16_t *dst, int width, int height);
> +
> +        void lastPixelInsert(uint16_t *src, uint16_t *dst, int width, int height);
> +
> +        void demosaic(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
> +                      int width, int height);
> +
> +        void autoWhiteBalance(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height);
> +
> +        void gammaCorrect(uint16_t *R, uint16_t *G, uint16_t *B, float val,
> +                          int width, int height);
> +
> +        float distance(int x, int y, int i, int j);
> +
> +        double gaussian(float x, double sigma);
> +
> +        void bilateralFilter(uint16_t *R, uint16_t *G, uint16_t *B,
> +                             int diameter, double sigmaI, double sigmaS,
> +                             int width, int height);
> +
> +        void compress_10bit_to_8bit(uint16_t *src, uint8_t *dst, int width, int height);    
> +
> +        void processing(FrameBuffer *srcBuffer, FrameBuffer *dstBuffer, int width, int height);
> +
> +        Signal<FrameBuffer *> ispCompleted;
> +
> +};
> +
> +}
> +
> +#endif /* __LIBCAMERA_PIPELINE_ISP_PROCESSING_H__ */
> -- 
> 2.20.1
>

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/isp/isp_processing.cpp b/src/libcamera/pipeline/isp/isp_processing.cpp
new file mode 100644
index 00000000..94e8081d
--- /dev/null
+++ b/src/libcamera/pipeline/isp/isp_processing.cpp
@@ -0,0 +1,593 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Siyuan Fan <siyuan.fan@foxmail.com>
+ *
+ * isp_processing.cpp - The software ISP class
+ */
+
+#include "isp_processing.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <libcamera/request.h>
+
+#include "libcamera/base/log.h"
+
+namespace libcamera{
+
+LOG_DECLARE_CATEGORY(ISP)
+
+void ISP::autoContrast(uint16_t *data, float lowCut, float highCut, int width, int height)
+{
+        int blue, gr, gb, red;
+        int histBlue[1024] = {0}, histGb[1024] = {0}, histGr[1024] = {0}, histRed[1024] = {0};
+
+        int index;
+        for (int i = 0; i < height; i++) {
+                index = i * width;
+                for (int j = 0; j < width; j++) {
+                        if (i % 2 == 0 && j % 2 == 0) {
+                                blue = data[index];
+                                histBlue[blue]++;
+                        }
+                        else if ((i % 2 == 0 && j % 2 == 1)) {
+                                gb = data[index];
+                                histGb[gb]++;
+                        }
+                        else if ((i % 2 == 1 && j % 2 == 0)) {
+                                gr = data[index];
+                                histGr[gr]++;
+                        }
+                        else {
+                                red = data[index];
+                                histRed[red]++;
+                        }
+                        index++;
+                }    
+        }
+
+        int pixelAmount = width * height;
+        int sum = 0;
+        int minBlue;
+        for (int i = 0; i < 1024; i++){
+                sum = sum + histBlue[i];
+                if (sum >= pixelAmount * lowCut * 0.01) {
+                        minBlue = i;
+                        break;
+                }
+        }
+
+        sum = 0;
+        int maxBlue;
+        for (int i = 1023; i >= 0; i--){
+                sum = sum + histBlue[i];
+                if (sum >= pixelAmount * highCut * 0.01) {
+                        maxBlue = i;
+                        break;
+                }
+        }
+
+        sum = 0;
+        int minGb;
+        for (int i = 0; i < 1024; i++){
+                sum = sum + histGb[i];
+                if (sum >= pixelAmount * lowCut * 0.01) {
+                        minGb = i;
+                        break;
+                }
+        }
+
+        sum = 0;
+        int maxGb;
+        for (int i = 1023; i >= 0; i--){
+                sum = sum + histGb[i];
+                if (sum >= pixelAmount * highCut * 0.01) {
+                        maxGb = i;
+                        break;
+                }
+        }
+
+        sum = 0;
+        int minGr;
+        for (int i = 0; i < 1024; i++){
+                sum = sum + histGr[i];
+                if (sum >= pixelAmount * lowCut * 0.01) {
+                        minGr = i;
+                        break;
+                }
+        }
+
+        sum = 0;
+        int maxGr;
+        for (int i = 1023; i >= 0; i--){
+                sum = sum + histGr[i];
+                if (sum >= pixelAmount * highCut * 0.01) {
+                        maxGr = i;
+                        break;
+                }
+        }
+
+        sum = 0;
+        int minRed;
+        for (int i = 0; i < 1024; i++){
+                sum = sum + histRed[i];
+                if (sum >= pixelAmount * lowCut * 0.01) {
+                        minRed = i;
+                        break;
+                }
+        }
+
+        sum = 0;
+        int maxRed;
+        for (int i = 1023; i >= 0; i--){
+                sum = sum + histRed[i];
+                if (sum >= pixelAmount * highCut * 0.01) {
+                        maxRed = i;
+                        break;
+                }
+        }
+
+        int blueMap[1024];
+        float norb = 1.0 / (maxBlue - minBlue);
+        for (int i = 0; i < 1024; i++) {
+                if (i < minBlue) {
+                        blueMap[i] = 0;
+                }
+                else if (i > maxBlue) {
+                        blueMap[i] = 1023;
+                }
+                else {
+                        blueMap[i] = (i - minBlue) * norb * 1023;
+                }
+                if (blueMap[i] > 1023) blueMap[i] = 1023;
+        }
+
+        int gbMap[1024];
+        float norgb = 1.0 / (maxGb - minGb);
+        for (int i = 0; i < 1024; i++) {
+                if (i < minGb) {
+                        gbMap[i] = 0;
+                }
+                else if (i > maxGb) {
+                        gbMap[i] = 1023;
+                }
+                else {
+                        gbMap[i] = (i - minGb) * norgb * 1023;
+                }
+                if (gbMap[i] > 1023) gbMap[i] = 1023;
+        }
+
+        int grMap[1024];
+        float norgr = 1.0 / (maxGr - minGr);
+        for (int i = 0; i < 1024; i++) {
+                if (i < minGr) {
+                        grMap[i] = 0;
+                }
+                else if (i > maxGr) {
+                        grMap[i] = 1023;
+                }
+                else {
+                        grMap[i] = (i - minGr) * norgr * 1023;
+                }
+                if (grMap[i] > 1023) grMap[i] = 1023;
+        }
+
+        int redMap[1024];
+        float norr = 1.0 / (maxRed - minRed);
+        for (int i = 0; i < 1024; i++) {
+                if (i < minRed) {
+                        redMap[i] = 0;
+                }
+                else if (i > maxRed) {
+                        redMap[i] = 1023;
+                }
+                else{
+                        redMap[i] = (i - minRed) * norr * 1023;
+                }
+                if (redMap[i] > 1023) redMap[i] = 1023;
+        }
+
+        for (int i = 0;i < height; i++) {
+                for (int j = 0; j < width; j++){
+                        index = i * width;
+                        if (i % 2 == 0 && j % 2 == 0) {
+                                data[index] = blueMap[data[index]];
+                        }    
+                        else if (i % 2 == 0 && j % 2 == 1) {
+                                data[index] = gbMap[data[index]];
+                        }    
+                        else if (i % 2 == 1 && j % 2 == 0) {
+                                data[index] = grMap[data[index]];
+                        }    
+                        else {
+                                data[index] = redMap[data[index]];
+                        }
+                        index++;
+                }
+        }
+}
+
+void ISP::blackLevelCorrect(uint16_t *data, uint16_t offset, int width, int height)
+{
+        int len = width * height;
+        for(int i = 0; i < len; i++) {
+                if (data[i] < offset){
+                        data[i] = 0;
+                }
+                else {
+                        data[i] -= offset;
+                }
+        }
+}
+
+void ISP::readChannels(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
+                       int width, int height)
+{
+        int index;
+        for (int i = 0; i < height; i++) {
+                index = i * width;
+                for (int j = 0; j < width; j++) {
+                        if (i % 2 == 0 && j % 2 == 0) {
+                                B[index] = data[index];
+                        }
+                        else if ((i % 2 == 0 && j % 2 == 1) || (i % 2 == 1 && j % 2 == 0)){
+                                G[index] = data[index];
+                        }
+                        else {
+                                R[index] = data[index];
+                        }
+                        index++;
+                }
+        }
+}
+
+void ISP::firstPixelInsert(uint16_t *src, uint16_t *dst, int width, int height)
+{
+        int index;
+        for (int i = 0; i < height; i++) {
+                index = i * width;
+                for (int j = 0; j < width; j++){
+                        if (i % 2 == 0 && j % 2 == 1) {
+                                if (j == (width - 1)) {
+                                        dst[index] = src[index - 1];
+                                }
+                                else {
+                                        dst[index] = (src[index - 1] +
+                                                src[index + 1]) >> 1;
+                                }
+                        }
+
+                        if (i % 2 == 1 && j % 2 == 0) {
+                                if(i == height - 1) {
+                                        dst[index] = src[index - width];
+                                }
+                                else {
+                                        dst[index] = (src[index - width]+
+                                                src[index + width]) >> 1;
+                                }
+                        }
+    
+                        if (i % 2 == 1 && j % 2 == 1) {
+                                if (j < width - 1 && i < height - 1) {
+                                        dst[index] = (src[index - width - 1] +
+                                                src[index - width + 1] +
+                                                src[index + width - 1] +
+                                                src[index + width + 1]) >> 2;
+                                }
+                                else if (i == height - 1 && j < width - 1) {
+                                        dst[index] = (src[index - width - 1] +
+                                                src[index - width + 1]) >> 1;
+                                }
+                                else if (i < height - 1 && j == width - 1) {
+                                        dst[index] = (src[index - width - 1] +
+                                                src[index + width - 1]) >> 1;
+                                }
+                                else {
+                                        dst[index] = src[index - width - 1];
+                                }          
+                        }
+                        index++;
+                }
+        }
+}
+
+void ISP::twoPixelInsert(uint16_t *src, uint16_t *dst, int width, int height)
+{
+        int index;
+        for (int i = 0; i < height; i++) {
+                index = i * width;
+                for (int j = 0; j < width; j++) {
+                        if (i == 0 && j == 0) {
+                                dst[index] = (src[index + width] +
+                                        src[index + 1]) >> 1;
+                        }
+                        else if (i == 0 && j > 0 && j % 2 == 0) {
+                                dst[index] = (src[index - 1] +
+                                        src[index + width] +
+                                        src[index + 1]) / 3;
+                        }
+                        else if (i > 0 && j == 0 && i % 2 == 0) {
+                                dst[index] = (src[index - width] +
+                                        src[index + 1] +
+                                        src[index + width]) / 3;
+                        }
+                        else if (i == (height - 1) && j < (width - 1) && j % 2 == 1) {
+                                dst[index] = (src[index - 1] +
+                                        src[index - width] +
+                                        src[index + 1]) / 3;
+                        }
+                        else if (i < (height - 1) && j == (width - 1) && i % 2 == 1) {
+                                dst[index] = (src[index - width] +
+                                        src[index - 1] +
+                                        src[index + width]) / 3;
+                        }
+                        else if (i == (height - 1) && j == (width - 1)) {
+                                dst[index] = (src[index - width] +
+                                        src[index - 1]) >> 1;
+                        }
+                        else if ((i % 2 == 0 && j % 2 == 0) || (i % 2 == 1 && j % 2 == 1)) {
+                                dst[index] = (src[index - 1] +
+                                        src[index + 1] +
+                                        src[index - width] +
+                                        src[index + width]) / 4;
+                        }
+                        index++;
+                }
+        }
+}
+
+void ISP::lastPixelInsert(uint16_t *src, uint16_t *dst, int width, int height)
+{
+        int index;
+        for (int i = 0; i < height; i++) {
+                index = i * width;
+                for (int j = 0; j < width; j++){
+                        if (i % 2 == 1 && j % 2 == 0) {
+                                if (j == 0) {
+                                        dst[index] = src[index + 1];
+                                }
+                                else {
+                                        dst[index] = (src[index - 1] +
+                                                src[index + 1]) >> 1;
+                                }
+                        }
+
+                        if (i % 2 == 0 && j % 2 == 1) {
+                                if(i == 0) {
+                                        dst[index] = src[index + width];
+                                }
+                                else {
+                                        dst[index] = (src[index - width]+
+                                                src[index + width]) >> 1;
+                                }
+                        }
+
+                        if (i % 2 == 0 && j % 2 == 0) {
+                                if (i > 0 && j > 0) {
+                                        dst[index] = (src[index - width - 1] +
+                                                src[index - width + 1] +
+                                                src[index + width - 1] +
+                                                src[index + width + 1]) >> 2;
+                                }
+                                else if (i == 0 && j > 0) {
+                                        dst[index] = (src[index + width - 1] +
+                                                src[index + width + 1]) >> 1;
+                                }
+                                else if (i > 0 && j == 0) {
+                                        dst[index] = (src[index - width + 1] +
+                                                src[index + width + 1]) >> 1;
+                                }
+                                else {
+                                        dst[index] = src[index + width + 1];
+                                }
+                        }
+                        index++;
+                }
+        }
+}
+
+void ISP::demosaic(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
+                   int width, int height)
+{
+        firstPixelInsert(data, B, width, height);
+        twoPixelInsert(data, G, width, height);
+        lastPixelInsert(data, R, width, height);
+}
+
+void ISP::autoWhiteBalance(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height)
+{
+        float aveB = 0, aveG = 0, aveR = 0;
+        float Kb, Kg, Kr;
+
+        for (int i = 0; i < width * height; i++) {
+                aveB += 1.0 * B[i];
+                aveG += 1.0 * G[i];
+                aveR += 1.0 * R[i];
+        }
+
+        aveB *= (1.0 / (width * height));
+        aveG *= (1.0 / (width * height));
+        aveR *= (1.0 / (width * height));
+
+        Kr = (aveB + aveG + aveR) / aveR * (1.0 / 3.0);
+        Kg = (aveB + aveG + aveR) / aveG * (1.0 / 3.0);
+        Kb = (aveB + aveG + aveR) / aveB * (1.0 / 3.0);
+
+        for (int i = 0; i < width * height; i++) {
+                B[i] = B[i] * Kb;
+                G[i] = G[i] * Kg;
+                R[i] = R[i] * Kr;
+
+                if (R[i] > 1023) R[i] = 1023;
+                if (G[i] > 1023) G[i] = 1023;
+                if (R[i] > 1023) B[i] = 1023;
+        }
+}
+
+void ISP::gammaCorrect(uint16_t *R, uint16_t *G, uint16_t *B, float val,
+                       int width, int height)
+{
+        float nor = 1.0 / 1023.0;
+        float gamma = 1.0 / val;
+        for (int i = 0; i < width * height; i++) {
+                R[i] = pow(R[i] * nor, gamma) * 1023;
+                G[i] = pow(G[i] * nor, gamma) * 1023;
+                B[i] = pow(B[i] * nor, gamma) * 1023;
+
+                if (R[i] > 1023) R[i] = 1023;
+                if (G[i] > 1023) G[i] = 1023;
+                if (B[i] > 1023) B[i] = 1023;
+        }
+}
+
+void ISP::compress_10bit_to_8bit (uint16_t *src, uint8_t *dst, int width, int height)
+{
+        for (int i = 0; i < width * height; i++) {
+                dst[i] = src[i] >> 2 & 0xff;
+        }
+}
+
+float ISP::distance(int x, int y, int i, int j)
+{
+    return float(sqrt(pow(x - i, 2) + pow(y - j, 2)));
+}
+
+double ISP::gaussian(float x, double sigma)
+{
+    return exp(-(pow(x, 2)) / (2 * pow(sigma, 2))) / (2 * 3.1415926 * pow(sigma, 2));
+}
+
+void ISP::bilateralFilter(uint16_t *R, uint16_t *G, uint16_t *B,
+                          int diameter, double sigmaI,
+                          double sigmaS, int width, int height)
+{
+        for (int i = 2; i < height - 2; i++) {
+                for (int j = 2; j < width - 2; j++) {
+                        double iFiltered = 0;
+                        double wp = 0;
+                        int neighbor_x = 0;
+                        int neighbor_y = 0;
+                        int half = diameter / 2;
+
+                        for (int k = 0; k < diameter; k++) {
+                                for (int l = 0; l < diameter; l++) {
+                                        neighbor_x = i - (half - k);
+                                        neighbor_y = j - (half - l);
+                                        double gi = gaussian(R[neighbor_x * width + neighbor_y] - R[i * width +j], sigmaI);
+                                        double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS);
+                                        double w = gi * gs;
+                                        iFiltered = iFiltered + R[neighbor_x * width + neighbor_y] * w;
+                                        wp = wp + w;
+                                }    
+                        }
+
+                        iFiltered = iFiltered / wp;
+                        R[i * width + j] = iFiltered;
+                }
+        }
+
+        for (int i = 2; i < height - 2; i++) {
+                for (int j = 2; j < width - 2; j++) {
+                        double iFiltered = 0;
+                        double wp = 0;
+                        int neighbor_x = 0;
+                        int neighbor_y = 0;
+                        int half = diameter / 2;
+
+                        for (int k = 0; k < diameter; k++) {
+                                for (int l = 0; l < diameter; l++) {
+                                        neighbor_x = i - (half - k);
+                                        neighbor_y = j - (half - l);
+                                        double gi = gaussian(G[neighbor_x * width + neighbor_y] - G[i * width +j], sigmaI);
+                                        double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS);
+                                        double w = gi * gs;
+                                        iFiltered = iFiltered + G[neighbor_x * width + neighbor_y] * w;
+                                        wp = wp + w;
+                                }    
+                        }
+
+                        iFiltered = iFiltered / wp;
+                        G[i * width + j] = iFiltered;
+                }
+        }
+
+        for (int i = 2; i < height - 2; i++) {
+                for (int j = 2; j < width - 2; j++) {
+                        double iFiltered = 0;
+                        double wp = 0;
+                        int neighbor_x = 0;
+                        int neighbor_y = 0;
+                        int half = diameter / 2;
+
+                        for (int k = 0; k < diameter; k++) {
+                                for (int l = 0; l < diameter; l++) {
+                                        neighbor_x = i - (half - k);
+                                        neighbor_y = j - (half - l);
+                                        double gi = gaussian(B[neighbor_x * width + neighbor_y] - B[i * width +j], sigmaI);
+                                        double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS);
+                                        double w = gi * gs;
+                                        iFiltered = iFiltered + B[neighbor_x * width + neighbor_y] * w;
+                                        wp = wp + w;
+                                }    
+                        }
+
+                        iFiltered = iFiltered / wp;
+                        B[i * width + j] = iFiltered;
+                }
+        }
+}
+
+void ISP::processing(FrameBuffer *srcBuffer, FrameBuffer *dstBuffer, int width, int height)
+{
+        uint8_t *rgb_buf;
+        uint16_t *rawData;
+        uint16_t *rgbData = new std::uint16_t[width * height * 3];
+
+        uint16_t *rData = rgbData;
+        uint16_t *gData = rData + width * height;
+        uint16_t *bData = gData + width * height;
+        memset(rgbData, 0x0, width * height * 3);
+
+        const FrameBuffer::Plane &plane =  srcBuffer->planes()[0]; 
+        rawData = (uint16_t *)mmap(NULL, plane.length, PROT_READ|PROT_WRITE, MAP_SHARED, plane.fd.fd(), 0);
+        if (rawData == MAP_FAILED) {
+            LOG(ISP, Error) << "Read raw data failed";
+            ispCompleted.emit(dstBuffer);
+        }
+
+        blackLevelCorrect(rawData, 16, width, height);
+        readChannels(rawData, rData, gData, bData, width, height);
+        demosaic(rawData, rData, gData, bData, width, height);
+        autoWhiteBalance(rData, gData, bData, width, height);
+        autoContrast(rData, 0.01, 0.01, width, height);
+        autoContrast(gData, 0.01, 0.01, width, height);
+        autoContrast(bData, 0.01, 0.01, width, height);
+        gammaCorrect(rData, gData, bData, 2.2, width, height);
+        //bilateralFilter(rData, gData, bData, 5, 24.0, 32.0);
+    
+        const FrameBuffer::Plane &rgbPlane = dstBuffer->planes()[0];
+        rgb_buf = (uint8_t *)mmap(NULL, rgbPlane.length, PROT_READ|PROT_WRITE, MAP_SHARED, rgbPlane.fd.fd(), 0);
+        if (rgb_buf == MAP_FAILED) {
+                LOG(ISP, Error) << "Read rgb data failed";
+                ispCompleted.emit(dstBuffer);
+        }
+
+        uint8_t *rData_8 = rgb_buf;
+        uint8_t *gData_8 = rData_8 + width * height;
+        uint8_t *bData_8 = gData_8 + width * height;
+
+        compress_10bit_to_8bit(rData, rData_8, width, height);
+        compress_10bit_to_8bit(gData, gData_8, width, height);
+        compress_10bit_to_8bit(bData, bData_8, width, height);
+
+        delete[] rgbData;
+
+        ispCompleted.emit(dstBuffer);
+}
+
+} /* namespace libcamera */
+
diff --git a/src/libcamera/pipeline/isp/isp_processing.h b/src/libcamera/pipeline/isp/isp_processing.h
new file mode 100644
index 00000000..b80b4218
--- /dev/null
+++ b/src/libcamera/pipeline/isp/isp_processing.h
@@ -0,0 +1,62 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Siyuan Fan <siyuan.fan@foxmail.com>
+ *
+ * isp_processing.h - The software ISP class
+ */
+#ifndef __LIBCAMERA_PIPELINE_ISP_PROCESSING_H__
+#define __LIBCAMERA_PIPELINE_ISP_PROCESSING_H__
+
+#include <libcamera/framebuffer.h>
+
+#include "libcamera/base/signal.h"
+#include "libcamera/base/object.h"
+
+namespace libcamera{
+
+using std::uint16_t;
+using std::uint8_t;
+
+class ISP : public Object
+{
+public:
+        void autoContrast(uint16_t *data, float lowCut, float highCut, int width, int height);
+
+        void blackLevelCorrect(uint16_t *data, uint16_t offset, int width, int height);
+
+        void readChannels(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
+                          int width, int height);
+
+        void firstPixelInsert(uint16_t *src, uint16_t *dst, int width, int height);
+
+        void twoPixelInsert(uint16_t *src, uint16_t *dst, int width, int height);
+
+        void lastPixelInsert(uint16_t *src, uint16_t *dst, int width, int height);
+
+        void demosaic(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B,
+                      int width, int height);
+
+        void autoWhiteBalance(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height);
+
+        void gammaCorrect(uint16_t *R, uint16_t *G, uint16_t *B, float val,
+                          int width, int height);
+
+        float distance(int x, int y, int i, int j);
+
+        double gaussian(float x, double sigma);
+
+        void bilateralFilter(uint16_t *R, uint16_t *G, uint16_t *B,
+                             int diameter, double sigmaI, double sigmaS,
+                             int width, int height);
+
+        void compress_10bit_to_8bit(uint16_t *src, uint8_t *dst, int width, int height);    
+
+        void processing(FrameBuffer *srcBuffer, FrameBuffer *dstBuffer, int width, int height);
+
+        Signal<FrameBuffer *> ispCompleted;
+
+};
+
+}
+
+#endif /* __LIBCAMERA_PIPELINE_ISP_PROCESSING_H__ */