Заливка кистью - PullRequest
       51

Заливка кистью

0 голосов
/ 19 мая 2018

Halo all!Я пытаюсь воссоздать Cut из PhotoDirector https://youtu.be/GLNNCrp650Y?t=77

И после нескольких переделок и подходов, это то, где я нахожусь. Автозаполнение

Базовый код отсюда: http://wiki.unity3d.com/index.php/TextureFloodFill

Базовый код взят из одного клика, используйте этот цвет и тесты роста, сравнивая исходный образец цветапротив любого другого пикселя и останавливает рост, если он не похож.

Так как мне нужно сэмплировать несколько пикселей (одним мазком кисти), я использовал для проверки вложенный класс bool (попытался добавить Colors в List ииспользуйте список. Содержит на каждый пиксель. СУПЕР медленный. Это намного быстрее)

В текущем прикрепленном коде я (обратно) использую RGB.Я использовал HSBColor раньше, но попытался вернуться к RGB, потому что в HSB есть случаи, когда, если Sat очень низкое или яркость слишком низкая / высокая, трудно сравнивать соседний оттенок этого пикселя.Я собираюсь вернуться к HSB и добавлю еще 2 вложенных bool, 1 only Sat> Brightness (для сэмплов с насыщенностью <порог, и все еще имеют яркость, поэтому в основном значения серого) Еще одна яркость (для очень темных / ярких) ЭтоТаким образом, при выборке выборка с низким насыщением отправляется в банк Sat> Bri, а низкая / высокая Bri направляется в банк Bri.При тестировании пикселей сначала проверьте Bri, если он может пропустить проверку Sat / Hue, или затем проверьте Sat, если он может пропустить (полную) проверку Hue.

Я не пробовал это (устал отвозвращаясь назад от HSB к RGB ...), потому что я начинаю думать (частично), что проблема не в проверке пикселей, а в проверке GROW .PhotoDirector (PD) также имеет действительно хорошее распознавание краев и может как-то устранить проблему фрагментации, которая у меня возникла.В моем коде я проверяю следующий ONE пиксель и останавливаюсь / продолжаю на этом основании.Может быть, PD проверяет FEW пикселей впереди, и если он постоянно отличается, то он фактически останавливается, и если он различается только на несколько пикселей, он просто растет над ними, таким образом, вынимает пиксели.

Еще одна вещь, которую имеет PD - это ограничивающая рамку функция ограничения.Мой код, даже маленький мазок кисти и маленький образец, если он совпадает и может вырасти до всего изображения, так и будет.ПД получил предел максимальной ширины / высоты роста.Я попробовал на однородном цветном изображении, и это действительно заполняет коробки.Я подумал, что, возможно, допуск ПД превышает лимит, НО с помощью этого ограничивающего прямоугольника, он может создать сплошную заливку, но в моем случае, если мой допуск слишком высок, он будет только расти по очевидным краям (и продолжается доimage ...) Так что я лично не добавил этот ограниченный предел и дождался, пока фактическая заливка будет более стабильной, особенно вблизи очищаемой области.

TL; DR In a "кисть к образцу, заливка заливки при нажатии «вверх», как в PhotoDirector, как они это делают?Это лучше выборка / цветное тестирование пикселей?Или также, как они увеличивают проверку пикселей?

abc

Спасибо за чтение!Приветствия,

Вот соответствующий код, вроде беспорядок, потому что он все еще постоянно меняется ..

public class GLookup
{
public bool[] bBools;

public GLookup(int num)
{
    bBools = new bool[num];
}

}

public class RLookup
{
public GLookup[] gLookup;

public RLookup(int num)
{
    gLookup = new GLookup[num];
    for(int i = 0; i < num; i++)
    {
        gLookup[i] = new GLookup(num);  
    }
}

}

 static void ResetLookup()
{
    for(int i = 0; i < Grid.Paint.Options.colorLookupRes; i++)
    {
        rgbLookup[i] = new RLookup(Grid.Paint.Options.colorLookupRes);
        rgbCheck[i] = new RLookup(Grid.Paint.Options.colorLookupRes);
    }
}

static void InsertLookup(Color32 rgb, float tol)
{
    int r = (int)(rgb.r/byteToLookup);// * Grid.Paint.Options.colorLookupRes);
    int g = (int)(rgb.g/ byteToLookup);// * Grid.Paint.Options.colorLookupRes);
    int b = (int)(rgb.b/ byteToLookup);// * Grid.Paint.Options.colorLookupRes);

    if(r == Grid.Paint.Options.colorLookupRes)
        r = Grid.Paint.Options.colorLookupRes - 1;
    if(g == Grid.Paint.Options.colorLookupRes)
        g = Grid.Paint.Options.colorLookupRes-1;
    if(b == Grid.Paint.Options.colorLookupRes)
        b = Grid.Paint.Options.colorLookupRes-1;

    if(rgbCheck[r].gLookup[g].bBools[b])
    {
        return;
    }
    rgbCheck[r].gLookup[g].bBools[b] = true;

    int tolNum = Grid.Paint.Options.maxTolNum;// (int)Mathf.Lerp(Grid.Paint.Options.minTolNum, Grid.Paint.Options.maxTolNum, tol);

    // Initial
    InsertLookupEntry(r, g, b);
    InsertNeighborSatBri(r, g, b, tolNum);

    // Hue fills
    int currentHue;
    // +- 5 hue if tol 1
    for(int i = 0; i < Grid.Paint.Options.hueFill; i++)
    {            
        // Higher hue
        currentHue = r + (i + 1);

        if(currentHue > Grid.Paint.Options.colorLookupRes-1)
            currentHue = Grid.Paint.Options.colorLookupRes-1;

        InsertLookupEntry(currentHue, g, b);
        InsertNeighborSatBri(currentHue, g, b, tolNum);

        // Lower hue
        currentHue = r - (i + 1);

        if(currentHue < 0)
            currentHue = 0;//            Grid.Paint.Options.colorLookupRes;

        InsertLookupEntry(currentHue, g, b);
        InsertNeighborSatBri(currentHue, g, b, tolNum);
    }
}
// This used to be for Hue/Sat/Brightness. But changed back to RGB, so this is similar as above's Hue tolerance fill

static void InsertNeighborSatBri(int r, int g, int b, int tolNum)
{
    int currentG;
    int currentB;
    for(int i = 0; i < tolNum; i++)
    {
        // Add the min plus from given value
        currentG = g + (i + 1);
        currentG = Mathf.Clamp(currentG, 0, Grid.Paint.Options.colorLookupRes - 1);
        for(int j = 0; j < tolNum; j++)
        {
            currentB = b + (j + 1);
            currentB = Mathf.Clamp(currentB, 0, Grid.Paint.Options.colorLookupRes - 1);
            InsertLookupEntry(r, currentG, currentB);

            currentB = b - (j + 1);
            currentB = Mathf.Clamp(currentB, 0, Grid.Paint.Options.colorLookupRes - 1);
            InsertLookupEntry(r, currentG, currentB);
        }

        currentG = g - (i + 1);
        currentG = Mathf.Clamp(currentG, 0, Grid.Paint.Options.colorLookupRes - 1);
        for(int j = 0; j < tolNum; j++)
        {
            currentB = b + (j + 1);
            currentB = Mathf.Clamp(currentB, 0, Grid.Paint.Options.colorLookupRes - 1);
            InsertLookupEntry(r, currentG, currentB);

            currentB = b - (j + 1);
            currentB = Mathf.Clamp(currentB, 0, Grid.Paint.Options.colorLookupRes - 1);
            InsertLookupEntry(r, currentG, currentB);
        }
    }
}

static void InsertLookupEntry(int r, int g, int b)
{
    rgbLookup[r].gLookup[g].bBools[b] = true;
}    

// Take in list of points from a single brush stroke
public static List<Point> FloodCutBrush(this Texture2D aTex, Color32[] refPixels, List<Point> growPoints, List<Point> lastDotSample, float tolerance, bool[] history, bool isAdd)
{
int w = aTex.width;

    int h = aTex.height;
    List<Point> changedPixel = new List<Point>();
    Color32[] photoPixels = new Color32[refPixels.Length];
    System.Array.Copy(refPixels, photoPixels, refPixels.Length);

    bool[] existCheck = new bool[history.Length];

    Queue<Point> nodes = new Queue<Point>();

    ResetLookup();

    // Setting up samples
    foreach(Point v in growPoints)
    {
        // Out of bounds is not valid
        if(v.x < 0 || v.y < 0 || w < 0)
            continue;

        int refId = (v.x) + ((v.y) * (w/2));
        if(refId >= photoPixels.Length)
            continue;

        Color32 hsb = photoPixels[refId];

        InsertLookup(hsb, tolerance);
        nodes.Enqueue(v);
    }

    // Fill search marks a pixel to alpha = 0. Then checks the top/bottom pixel and adds that to queue for future iteration
    while(nodes.Count > 0)
    {
        Point current = nodes.Dequeue();

//this goes right


for(int i = current.x; i < w / 2; i++)
        {
            int idx = i + current.y * (w / 2);
            if(idx >= history.Length)
                continue;

            Point fullCoord = IndexToPoint(w / 2, idx);
            // Because color sampling from brush is half resolution, and photopixel is full resolution..
            fullCoord.x *= 2;
            fullCoord.y *= 2;
            int fullIdx = fullCoord.x + (fullCoord.y * (w));

            Color32 C = photoPixels[fullIdx];

            if(history[idx] && (C.a == 0 || RGBTest(C) == false))// && (C.b < highBright && C.b > lowBright))
            {
                changedPixel.Add(current);
                existCheck[idx] = true;
                break;
            }

            C.a = 0;
            photoPixels[fullIdx] = C;
            // Queue up/down pix
            if(current.y + 1 < h / 2)
            {
                C = photoPixels[fullIdx + w];
                if(history[idx] && (C.a != 0 && RGBTest(C) == true))// || (C.b > highBright || C.b < lowBright))
                {
                    nodes.Enqueue(new Point(i, current.y + 1));

                }
            }

            if(current.y - 1 >= 0)
            {
                C = photoPixels[fullIdx - w];
                if(history[idx] && (C.a != 0 && RGBTest(C))) // || (C.b > highBright || C.b < lowBright))
                {
                    nodes.Enqueue(new Point(i, current.y - 1));

                }
            }

            changedPixel.Add(current);
            existCheck[idx] = true;

        }

        //this goes left, same as above but --x


    }

    return changedPixel;
}

public static bool RGBTest(Color32 c1)
    {
    int r = (int)(c1.r/ byteToLookup);
    int g = (int)(c1.g/ byteToLookup);
    int b = (int)(c1.b/ byteToLookup);

    if(rgbLookup[r].gLookup[g].bBools[b])
    {
        count++;
        return true;
    } else
        return false;

}
...