WS2812b - Интерполировать пиксели на неопиксельной матрице с маской

упростил мою цель — отобразить переходный фон и некоторые фигуры. Эти фигуры не сильно растеризованы на 1 пиксель, больше похоже на 0,1 каждый цикл. Поэтому мне нужен способ интерполировать движение между окружающими неопикселями.

Фон работает нормально, так что сейчас это неактуально.

Для фигур первой попыткой было показать плавное движение квадрата, жестко запрограммированного. Это работает подходящим образом, даже если оно не содержит ошибок. Я поставил Код первой попытки в самый конец.

После этого я хотел реализовать растровую маску, чтобы показать что-то более сложное. Вот где я нахожусь в настоящее время. Первая попытка состоит в том, чтобы предположить, что вся маска полностью заполнена (опять же жестко закодирована).

Светодиоды в большинстве случаев постоянно горят или выключаются.

Как сделать так, чтобы они исчезали с внешней стороны?

Я использую AI Thinker ESP 32 и 200 WS2812B.

Пожалуйста, запросите любую дополнительную информацию, которая требуется. Конечно, если вы обнаружите, что мой подход не подходит для начала, пожалуйста, сообщите мне.

Информация для кода: не заблудитесь в "круге" переменные. На первых шагах я показывал круг и вскоре понял, что легче вернуться к кругу, работая над интерполяцией.

Мой код с растровым изображением

#include <Adafruit_NeoPixel.h>

#define PIN_NEO_PIXEL  13
#define NUM_PIXELS     400
#define num_row 20
#define num_col 20
#define circle_size_body 4
#define circle_size_border 5
#define circle_size_corona 7
#define DELAY_INTERVAL  100

float circle_PosX = 10;
float circle_PosY = 10;
int circle_direction = 1;
enum eIntersectingPart { None, Body, Border, Corona };

Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);

void setup()
{
  NeoPixel.setBrightness(100);
  NeoPixel.begin();
  Serial.begin(115200);           // настраиваем последовательную библиотеку на 9600 бит/с
}

float backgroundShift = 0;
float worldSpeed = .05;

void loop()
{
  if(circle_PosY <= 0 && circle_direction < 0
     || circle_PosY >= 10 && circle_direction > 0)
    circle_direction *= -1;
  circle_PosY += worldSpeed * circle_direction;
  circle_PosX += worldSpeed * circle_direction;
    Serial.print("circle_PosX ");
    Serial.print(circle_PosX);
    Serial.print(", circle_PosY ");
    Serial.print(circle_PosY);
    NeoPixel.clear();
    //Hintergrund setzen
    // setBackgroundColor();
        
    //Крайс мален
    drawCircle();
  
  // Serial.print("bgShift: ");
  // Serial.flush();
  NeoPixel.show(); // отправляем обновленные цвета пикселей на оборудование NeoPixel.
  // Serial.println(backgroundShift);
  backgroundShift += worldSpeed;
  delay(100);     // время отключения
}

uint32_t setBackgroundColor() {
    
  for(int led = 0; led < NUM_PIXELS; led++) {
    int coordinates[2];
    coordinateByLed(coordinates, led);
    
    byte ambientColors[3] = { 0, 0, 255 };
    int ambientCcolorS = (int)( sin( 0.3 * coordinates[0] + backgroundShift) * 127.0 );
    int ambientCcolorC = (int)( cos( 0.3 * coordinates[1] + backgroundShift) * 128.0);
    int amberC = (ambientCcolorS + ambientCcolorC + 255) / 2;
    
    NeoPixel.setPixelColor(
        led,
        NeoPixel.Color(amberC, amberC ,255));
  }
}

bool firstIteration = false;
void drawCircle() {
    // ху Антейл
    byte bitmap[circle_size_corona + 1][circle_size_corona + 1];
    
    float startPositonX = circle_PosX - .5f * (float)circle_size_corona;
    float startPositonY = circle_PosY - .5f * (float)circle_size_corona;
    
    int lowerStartX = floor(startPositonX);
    int upperStartX = ceil(startPositonX);
    int lowerStartY = floor(startPositonY);
    int upperStartY = ceil(startPositonY);
            
    float proportionForLowerX = abs(upperStartX - startPositonX);
    float proportionForUpperX = 1 - proportionForLowerX;
    float proportionForLowerY = abs(upperStartY - startPositonY);
    float proportionForUpperY = 1 - proportionForLowerY;
    
    firstIteration = true;
    
    for(byte x = 0; x < circle_size_corona; x++) {
        for(byte y = 0; y < circle_size_corona; y++) {
            float proportion = 0;
            
            int lowerLedX = lowerStartX + x;
            int lowerLedY = lowerStartY + y;
            int upperLedX = upperStartX + x;
            int upperLedY = upperStartY + y;
            
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                byte currentX = x;
                byte currentY = y;
                proportion = proportionForLowerX * proportionForLowerY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                
                if(lowerLedX == 6 && lowerLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                byte currentX = x + 1;
                byte currentY = y;
                proportion = proportionForUpperX * proportionForLowerY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                if(upperLedX == 6 && lowerLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                byte currentX = x;
                byte currentY = y + 1;
                proportion = proportionForLowerX * proportionForUpperY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                if(lowerLedX == 6 && upperLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                byte currentX = x + 1;
                byte currentY = y + 1;
                proportion = proportionForUpperX * proportionForUpperY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                if(upperLedX == 6 && upperLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
    

            firstIteration = false;
        }
    }
    
    firstIteration = true;
    for(int x = 0; x < circle_size_corona + 1; x++) {
        for(int y = 0; y < circle_size_corona + 1; y++) {
            int lowerLedX = lowerStartX + x;
            int lowerLedY = lowerStartY + y;
            int upperLedX = upperStartX + x;
            int upperLedY = upperStartY + y;
            
            if(
            // первая итерация
                lowerLedX == 6 && lowerLedY == 6
                  ||
                    upperLedX == 6 && lowerLedY == 6) {
                Serial.print(", startPositonX ");
                Serial.print(startPositonX);
                Serial.print(", x ");
                Serial.print(x);
                Serial.print(", y ");
                Serial.print(y);
                Serial.print(", lowerLedX ");
                Serial.print(lowerLedX);
                Serial.print(", lowerLedY ");
                Serial.print(lowerLedY);
            }
            
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                setLedColorWithProportion(lowerLedX,lowerLedY, .01f * bitmap[x][y]);
            }
            
            firstIteration = false;
        }
    }
    
    Serial.print(" ");
    Serial.println();
}

void setLedColorWithProportion(
    int coordX, int coordY,
    float proportion)
{
    int led = ledByCoordinate(coordX, coordY);
    uint32_t ledColor = NeoPixel.getPixelColor(led);
    uint8_t partialColor = (uint8_t)((proportion ) * 255.0f );
    
    uint8_t white = (ledColor>>24) & 255;
    uint8_t red = (uint8_t)((1.0f - proportion) * ((ledColor>>16) & 255))
        + partialColor;
    uint8_t green = (ledColor>>8) & 255;
    uint8_t blue = ledColor & 255;
    
    if(
    // первая итерация
    coordX == 6 && coordY == 6
    ) {
        Serial.print(", prop ");
        Serial.print(proportion);
        Serial.print(", redValOld ");
        Serial.print(((ledColor>>16) & 255));
        Serial.print(", redValNew ");
        Serial.print(red);
    }
    
    ledColor = white;
    ledColor = (ledColor<<8) + red;
    ledColor = (ledColor<<8) + green;
    ledColor = (ledColor<<8) + blue;
    NeoPixel.setPixelColor(
        led,
        ledColor);
}

int ledByCoordinate(byte column, byte row) {
  return (row  * num_col) + (column);
}

void coordinateByLed(int coordinates[2], int led) {
  coordinates[0] = led % num_col;
  coordinates[1] = led / num_col;
}

neoopixel


Первая попытка без растрового изображения

#include <Adafruit_NeoPixel.h>

#define PIN_NEO_PIXEL  13
#define NUM_PIXELS     400
#define num_row 20
#define num_col 20
#define circle_size_body 4
#define circle_size_border 5
#define circle_size_corona 7
#define DELAY_INTERVAL  100

float circle_PosX = 10;
float circle_PosY = 10;
int circle_direction = 1;
enum eIntersectingPart { None, Body, Border, Corona };

Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);

void setup()
{
  NeoPixel.setBrightness(100);
  NeoPixel.begin();
  Serial.begin(9600);           // настраиваем последовательную библиотеку на 9600 бит/с
}

float backgroundShift = 0;
float worldSpeed = .01;

void loop()
{
  if(circle_PosY <= 0 && circle_direction < 0
     || circle_PosY >= 10 && circle_direction > 0)
    circle_direction *= -1;
  circle_PosY += worldSpeed * circle_direction;
  circle_PosX += worldSpeed * circle_direction;
    // Serial.print("circle_PosX");
    // Serial.print(circle_PosX);
    // Serial.print(", circle_PosY ");
    // Serial.print(circle_PosY);
    NeoPixel.clear();
    //Hintergrund setzen
    // setBackgroundColor();
        
    //Крайс мален
    drawCircle();
  
  // Serial.print("bgShift: ");
  // Serial.flush();
  NeoPixel.show(); // отправляем обновленные цвета пикселей на оборудование NeoPixel.
  // Serial.println(backgroundShift);
  backgroundShift += worldSpeed;
  // задержка (100); // время отключения
}

uint32_t setBackgroundColor() {
    
  for(int led = 0; led < NUM_PIXELS; led++) {
    int coordinates[2];
    coordinateByLed(coordinates, led);
    
    byte ambientColors[3] = { 0, 0, 255 };
    int ambientCcolorS = (int)( sin( 0.3 * coordinates[0] + backgroundShift) * 127.0 );
    int ambientCcolorC = (int)( cos( 0.3 * coordinates[1] + backgroundShift) * 128.0);
    int amberC = (ambientCcolorS + ambientCcolorC + 255) / 2;
    
    NeoPixel.setPixelColor(
        led,
        NeoPixel.Color(amberC, amberC ,255));
  }
}

bool firstIteration = false;
void drawCircle() {
    float startPositonX = circle_PosX - .5f * (float)circle_size_corona;
    float startPositonY = circle_PosY - .5f * (float)circle_size_corona;
    
    int lowerStartX = floor(startPositonX);
    int upperStartX = ceil(startPositonX);
    int lowerStartY = floor(startPositonY);
    int upperStartY = ceil(startPositonY);
            
    float proportionForLowerX = abs(upperStartX - startPositonX);
    float proportionForUpperX = 1 - proportionForLowerX;
    float proportionForLowerY = abs(upperStartY - startPositonY);
    float proportionForUpperY = 1 - proportionForLowerY;
    
    // int endX = lowerStartX + circle_size_corona;
    // int endY = lowerStartY + circle_size_corona;
    
    firstIteration = true;
    
    for(int x = 0; x < circle_size_corona; x++) {
        for(int y = 0; y < circle_size_corona; y++) {
            int lowerLedX = lowerStartX + x;
            int lowerLedY = lowerStartY + y;
            int upperLedX = upperStartX + x;
            int upperLedY = upperStartY + y;
            
            /*if(firstIteration) {
                Serial.print(", startPositonX ");
                Serial.print(startPositonX);
                Serial.print(", x ");
                Serial.print(x);
                Serial.print(", y ");
                Serial.print(y);
                Serial.print(", lowerLedX ");
                Serial.print(lowerLedX);
                Serial.print(", lowerLedY ");
                Serial.print(lowerLedY);
            }*/
            
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                    setLedColorWithProportion(lowerLedX,lowerLedY, proportionForLowerX * proportionForLowerY);
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                    setLedColorWithProportion(upperLedX,lowerLedY, proportionForUpperX * proportionForLowerY);
            }
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                    setLedColorWithProportion(lowerLedX,upperLedY, proportionForLowerX * proportionForUpperY);
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                    setLedColorWithProportion(upperLedX,upperLedY, proportionForUpperX * proportionForUpperY);
            }
                
            firstIteration = false;
        }
    }
    // Serial.print(" ");
    // Serial.println();
}

void setLedColorWithProportion(
    int coordX, int coordY,
    float proportion)
{
    int led = ledByCoordinate(coordX, coordY);
    uint32_t ledColor = NeoPixel.getPixelColor(led);
    uint8_t partialColor = (uint8_t)((proportion ) * 255.0f );
    
    uint8_t white = (ledColor>>24) & 255;
    uint8_t red = (uint8_t)((1.0f - proportion) * ((ledColor>>16) & 255))
        + partialColor;
    uint8_t green = (ledColor>>8) & 255;
    uint8_t blue = ledColor & 255;
    
    ledColor = white;
    ledColor = (ledColor<<8) + red;
    ledColor = (ledColor<<8) + green;
    ledColor = (ledColor<<8) + blue;
    NeoPixel.setPixelColor(
        led,
        ledColor);
}

int ledByCoordinate(byte column, byte row) {
    
    /*if(firstIteration) {
        Serial.print(", column ");
        Serial.print(column);
        Serial.print(", row ");
        Serial.print(row);
        Serial.print(", ergebis ");
        Serial.print(((row  * num_col) + (column)));
    }*/
  return (row  * num_col) + (column);
}

void coordinateByLed(int coordinates[2], int led) {
  coordinates[0] = led % num_col;
  coordinates[1] = led / num_col;
}

, 👍1

Обсуждение

Что именно вы имеете в виду под растровым изображением и маской здесь? Вы пытались интерполировать, перемещая каждый компонент линейно с положением прямоугольника? Где именно у вас проблемы?, @chrisl

может быть проще сначала разработать код для одной строки .... например, если есть 5 полностью освещенных пикселей, а движение идет слева направо, то 1-й пиксель должен исчезать, а 6-й пиксель должен исчезать. ... остальные 4 пикселя остаются неизменными, @jsotola

Похоже, вы спрашиваете о «сглаживании»., @timemage

Вероятно, это не связано с вашей проблемой, но setLedColorWithProportion() — довольно нетрадиционный способ сделать [альфа-композитинг](https://en.wikipedia.org/wiki/Alpha_compositing): объект переднего плана прозрачен во всех каналах, кроме красного. . Обычно ожидается, что непрозрачность будет одинаковой для всех каналов., @Edgar Bonet


1 ответ


1

Спасибо за совет. Я вернулся к 1D-модели, упростил, изменил, и она заработала со сложными моделями (в 1D XD), жестко закодированным массивом. Итак, наконец, я перешел к 2D и расширил код. Теперь это работает.

Да, это своего рода сглаживание.

Дальше я думаю, как отобразить несколько элементов с прозрачностью (вызванной сглаживанием или заданной прозрачностью).

На этом пока все.

Для всех, кому это может быть интересно, вот мой текущий Кодекс.

#include <Adafruit_NeoPixel.h>

#define PIN_NEO_PIXEL  13
#define NUM_PIXELS     200
#define num_col 20
#define num_row 10
#define DELAY_INTERVAL  100

float circle_PosX = 6;
float circle_PosY = 6;
int circle_direction = 1;

Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);

void setup()
{
  NeoPixel.setBrightness(255);
  NeoPixel.begin();
  // Serial.begin(115200); // настраиваем последовательную библиотеку на 9600 бит/с
}

float worldSpeed = .01;

void loop()
{
    if(circle_PosX <= 0 && circle_direction < 0
     || circle_PosX >= 20 && circle_direction > 0
     || circle_PosY <= 0 && circle_direction < 0
     || circle_PosY >= 10 && circle_direction > 0)
    circle_direction *= -1;
    circle_PosX += worldSpeed * circle_direction;
    circle_PosY += worldSpeed * circle_direction;
  
    NeoPixel.clear();
        
    //Крайс мален
    drawCircle();

    NeoPixel.show(); // отправляем обновленные цвета пикселей на оборудование NeoPixel.
    // задержкамикросекунд(500); // время отключения
    // чтение ввода();
}
uint8_t bitmap[5][5] = {
    {0,0,1,0,0},
    {0,1,0,1,0},
    {1,0,0,0,1},
    {0,1,0,1,0},
    {0,0,1,0,0},
};

void drawCircle() {
    for(uint8_t pixelX = 0; pixelX < 5; pixelX++) {
        for(uint8_t pixelY = 0; pixelY < 5; pixelY++) {
            if(bitmap[pixelY][pixelX] == 1) {
                writeCircleToLEDs(
                    pixelX + circle_PosX,
                    pixelY + circle_PosY);
            }
        }
    }
}

uint8_t writeCircleToLEDs(float circlePosX, float circlePosY) {
    int lowerLedX = floor(circlePosX);
    int upperLedX = ceil(circlePosX);
    int lowerLedY = floor(circlePosY);
    int upperLedY = ceil(circlePosY);
    
    float upperLedXAmount = circlePosX - lowerLedX;
    float lowerLedXAmount = 1.0 - upperLedXAmount;
    float upperLedYAmount = circlePosY - lowerLedY;
    float lowerLedYAmount = 1.0 - upperLedYAmount;

    float amountLowerXLowerY = lowerLedXAmount * lowerLedYAmount;
    float amountUpperXLowerY = upperLedXAmount * lowerLedYAmount;
    float amountLowerXUpperY = lowerLedXAmount * upperLedYAmount;
    float amountUpperXUpperY = upperLedXAmount * upperLedYAmount;
    
    setColorOfLed(lowerLedX, lowerLedY, amountLowerXLowerY);
        
    // если (нижний светодиодX != верхний светодиодX)
    {
        setColorOfLed(upperLedX, lowerLedY, amountUpperXLowerY);
    }
    // если (нижний светодиод Y != нижний светодиод X )
    {
        setColorOfLed(lowerLedX, upperLedY, amountLowerXUpperY);
    }
    // если (нижний светодиод != верхний светодиод )
    {
        setColorOfLed(upperLedX, upperLedY, amountUpperXUpperY);
    }
    /*
    Serial.print("circlePosX ");
    Serial.println(circlePosX);
    Serial.print("lowerLedX ");
    Serial.println(lowerLedX);
    Serial.print("lowerLedXAmount ");
    Serial.println(lowerLedXAmount);
    Serial.println("--------------------");*/
    
}

void setColorOfLed(uint8_t ledX, uint8_t ledY, float amount) {
    int ledNumber = ledByCoordinate(ledX, ledY);
    uint32_t  baseColor = NeoPixel.getPixelColor(ledNumber);
    uint8_t newRedColor = ((baseColor >> 16) & 255);
    newRedColor += (255 * amount);
    
    baseColor &= ~(255 << 16);
    baseColor |= (newRedColor << 16);
    
    NeoPixel.setPixelColor(
        ledNumber,
        baseColor);
}

uint8_t ledByCoordinate(uint8_t coordinateX, uint8_t coordinateY) {
    return coordinateX + num_col * coordinateY;
}

uint8_t rowReadPosY = 0;
void readInput() {
    bool changed = false;
    uint8_t rowReadPosX = 0;
    
    while(Serial.available() > 0) {
        char input = Serial.read();
        
        if(!isDigit(input)) continue;
        
        bitmap[rowReadPosY][rowReadPosX] = atoi(&input);
        
        rowReadPosX++;
        if(rowReadPosX >= 5) {
            rowReadPosY = ++rowReadPosY % 5;
            rowReadPosX = 0;
        }
        
        changed = true;
    }
    
    if(changed) {
        Serial.println("Ergebnis: ");
        
        for(uint8_t pixelX = 0; pixelX < 5; pixelX++) {
            for(uint8_t pixelY = 0; pixelY < 5; pixelY++) {
                Serial.print(bitmap[pixelY][pixelX]);
            }
            Serial.println();
        }
        
        Serial.print("Aktueller Cursor: (x,y) {");
        Serial.print(rowReadPosX);
        Serial.print(", ");
        Serial.print(rowReadPosY);
        Serial.println("}");
    }
}
,