[OpenCv] [C ++] Создание гладкого, похожего на аэрографию мазка, похожего на Photoshop? - PullRequest
0 голосов
/ 23 октября 2019

У меня круглая кисть диаметром 200 пикселей и твердостью 0 (кисть имеет круговой градиент). Расстояние между каждой щеткой составляет 25% от диаметра кисти. Однако, когда я сравниваю обводку, моя программа рисует и обводку, которую рисует Photoshop, где все настройки равны ... enter image description here Ясно, что в Photoshop все гораздо плавнее! Я не могу уменьшить интервал, потому что это приводит к тому, что края становятся более жесткими

Как я могу сделать мой ход как в фотошопе? Вот соответствующий код из моей программы ...

//defining a circle
Mat alphaBrush(2*outerRadius,2*outerRadius,CV_32FC1);
float floatInnerRadius = outerRadius * hardness;
for(int i = 0; i < alphaBrush.rows; i++ ){
    for(int j=0; j<alphaBrush.cols; j++ ){
        int x = outerRadius - i;
        int y = outerRadius - j;
        float radius=hypot((float) x, (float) y );
        auto& pixel = alphaBrush.at<float>(i,j);
        if(radius>outerRadius){ pixel=0.0; continue;}      // transparent
        if(radius<floatInnerRadius){ pixel=1.0; continue;}      // solid
        pixel=1-((radius-floatInnerRadius)/(outerRadius-floatInnerRadius));  // partial
    }
}

/*
(...irrelevant stuff)
*/

//drawing the brush onto the canvas
for (int j = 0; j < inMatROI.rows; j++) {
            Vec3b *thisBgRow = inMatROI.ptr<Vec3b>(j);
            float *thisAlphaRow = brushROI.ptr<float>(j);
            for (int i = 0; i < inMatROI.cols; i++) {
                for (int c = 0; c < 3; c++) {
                    thisBgRow[i][c] = saturate_cast<uchar>((brightness * thisAlphaRow[i]) + ((1.0 - thisAlphaRow[i]) * thisBgRow[i][c]));
                }
            }
        }

Я также пробовал resultValue = max (backgroundValue, brushValue), но пересечение двух окружностей довольно очевидно.

1 Ответ

0 голосов
/ 23 октября 2019

это подход, рисующий сплошную тонкую линию и затем вычисляющий расстояние каждого пикселя до этой линии. Как вы можете видеть, есть некоторые артефакты, вероятно, в основном из-за только приблизительных значений расстояния из cv :: distanceTransform. Если вы точно вычислите расстояния (и, возможно, с двойной точностью), вы получите очень плавные результаты.

enter image description here

int main()
{
    cv::Mat canvas = cv::Mat(768, 768, CV_8UC3, cv::Scalar::all(255));

    cv::Mat canvasMask = cv::Mat::zeros(canvas.size(), CV_8UC1);

    // make sure the stroke has always a size of >= 2, otherwise will be cv::line way not work...
    std::vector<cv::Point> strokeSampling;
    strokeSampling.push_back(cv::Point(250, 100));

    strokeSampling.push_back(cv::Point(250, 200));
    strokeSampling.push_back(cv::Point(600, 300));
    strokeSampling.push_back(cv::Point(600, 400));
    strokeSampling.push_back(cv::Point(250, 500));

    strokeSampling.push_back(cv::Point(250, 650));

    for (int i = 0; i < strokeSampling.size() - 1; ++i)
        cv::line(canvasMask, strokeSampling[i], strokeSampling[i + 1], cv::Scalar::all(255));

    // computing a distance map:
    cv::Mat tmp1 = 255 - canvasMask;
    cv::Mat distMap;
    cv::distanceTransform(tmp1, distMap, CV_DIST_L2, CV_DIST_MASK_PRECISE);

    float outerRadius = 50;
    float innerRadius = 10;
    cv::Scalar strokeColor = cv::Scalar::all(0);

    for (int y = 0; y < distMap.rows; ++y)
        for (int x = 0; x < distMap.cols; ++x)
        {
            float percentage = 0.0f;
            float radius = distMap.at<float>(y, x);

            if (radius>outerRadius){ percentage = 0.0; }      // transparent
            else
            if (radius<innerRadius){ percentage = 1.0; }      // solid
            else
            {
                percentage = 1 - ((radius - innerRadius) / (outerRadius - innerRadius));  // partial
            }

            if (percentage > 0)
            {
                // here you could use the canvasMask if you like to, instead of directly drawing on the canvas
                cv::Vec3b canvasColor = canvas.at<cv::Vec3b>(y, x);
                cv::Vec3b cColor = cv::Vec3b(strokeColor[0], strokeColor[1], strokeColor[2]);
                canvas.at<cv::Vec3b>(y, x) = percentage*cColor + (1 - percentage) * canvasColor;
            }
        }

    cv::imshow("out", canvas);
    cv::imwrite("C:/StackOverflow/Output/stroke.png", canvas);
    cv::waitKey(0);

}
...