直播代碼圖像轉鉛筆素描方法

#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
 
 
//#define DEBUG_INFO
 
void generate_conv_kernel(int ksize, float angle, int hardness, cv::Mat &kernel)
{
    int halfSize = ksize / 2;
    int size = halfSize * 2 - 1;
    int y0 = halfSize - hardness + 1;
    int y1 = halfSize + hardness - 1;
 
    cv::Mat rect, rotateMat;
    cv::Point2i central;
 
    kernel = cv::Mat(size, size, CV_32FC1, cv::Scalar(0));
 
    rect = cv::Mat(kernel, cv::Rect(0, y0, size, y1 - y0 + 1));
    rect = cv::Scalar(255);
 
    central = cv::Point2i(halfSize, halfSize);
    rotateMat = cv::getRotationMatrix2D(central, angle, 1.0);
 
    cv::warpAffine(kernel, kernel, rotateMat, kernel.size());
}
 
 
void generate_stroke_structure(cv::Mat &gray, int hardness, int directions, float strength, cv::Mat &structure)
{
    cv::Mat gradient;
    cv::Mat *kernels = new cv::Mat[directions];
    cv::Mat *Gs = new cv::Mat[directions];
    cv::Mat *Cs = new cv::Mat[directions];
 
    int rows, cols;
    int ksize;
 
    assert(gray.depth() == CV_32F);
 
    rows = gray.rows;
    cols = gray.cols;
 
    {
        cv::Mat gx, gy;
        cv::Sobel(gray, gx, CV_32FC1, 1, 0);
        cv::Sobel(gray, gy, CV_32FC1, 0, 1);
        cv::magnitude(gx, gy, gradient);
 
#ifdef DEBUG_INFO
        cv::imshow("gradient", gradient/255);
        cv::waitKey();
#endif
    }
 
    ksize = (rows < cols ? rows : cols) / 30;
 
    for(int i = 0; i < directions; i++)
    {
        float angle = i * 180.0 / directions;
 
        generate_conv_kernel(ksize, angle, hardness, kernels[i]);
        cv::filter2D(gradient, Gs[i], -1, kernels[i]);
 
        Cs[i] = cv::Mat(rows, cols, CV_32FC1, cv::Scalar(0));
    }
 
    for(int y = 0; y < rows; y++)
    {
        for(int x = 0; x < cols; x++)
        {
            int idx = 0;
            float mmax = 0, t;
 
            for(int i = 0; i < directions; i++)
            {
                if( (t = Gs[i].at<float>(y, x)) > mmax)
                {
                    mmax = t;
                    idx = i;
                }
            }
 
            Cs[idx].at<float>(y, x) = gradient.at<float>(y, x);
        }
    }
 
 
    structure = cv::Mat(rows, cols, CV_32FC1, cv::Scalar(0));
 
    for(int i = 0; i < directions; i++)
    {
        cv::filter2D(Cs[i], Cs[i], -1, kernels[i]);
        structure += Cs[i];
    }
 
    cv::normalize(structure, structure, 0, 1, cv::NORM_MINMAX);
    structure = 1 - structure * strength;
 
    delete[] kernels;
    delete[] Gs;
    delete[] Cs;
 
}
 
 
void calc_hist(uchar *data, int width, int height, int stride, float hist[])
{
    float sq = width * height;
 
    memset(hist, 0, sizeof(float) * 256);
 
    for(int y = 0; y < height; y++)
    {
        for(int x = 0; x < width; x++)
            hist[data[x]] ++;
        data += stride;
    }
 
    for(int i = 0; i < 256; i++)
        hist[i] /= sq;
}
 
 
void specific_hist(float *hist, float *target, float histMap[])
{
    float cumHist[256], dist[256 * 256], *ptr;
    int start = 0, end = 0, lastStart = 0, lastEnd = 0;
 
    cumHist[0] = hist[0];
 
    for(int i = 1; i < 256; i++)
        cumHist[i] = cumHist[i-1] + hist[i];
 
 
    ptr = dist;
    for(int y = 0; y < 256; y++)
    {
        for(int x = 0; x < 256; x++)
            ptr[x] = fabs(cumHist[x] - target[y]);
 
        ptr += 256;
    }
 
    ptr = dist;
    for(int y = 0; y < 256; y++)
    {
        float mmin = ptr[0];
 
        for(int x = 1; x < 256; x++)
        {
            if(mmin >= ptr[x])
            {
                mmin = ptr[x];
                end = x;
            }
        }
 
        if(start != lastStart || end != lastEnd)
        {
            for(int x = start; x <= end; x++)
                histMap[x] = y;
 
            lastStart = start;
            lastEnd = end;
            start = end + 1;
        }
 
        ptr += 256;
    }
 
}
 
 
void generate_tone(cv::Mat &gray, float hist[], cv::Mat &tone)
{
    float target[256], bright[256], dark[256], mid[256];
    float sumB = 0, sumD = 0, sumM = 0, sum = 0;
 
    int rows = gray.rows;
    int cols = gray.cols;
 
    tone = cv::Mat(rows, cols, CV_32FC1, cv::Scalar(0));
 
    for(int i = 0; i < 256; i++)
    {
        bright[i] = expf((i - 255.0) / 9);
        dark[i] = expf( (i - 90) * (i - 90) / -242.0);
 
        sumB += bright[i];
        sumD += dark[i];
 
        if( 105 <= i && i <= 225)
            mid[i] = 1;
        else
            mid[i] = 0;
 
        sumM += mid[i];
    }
 
    sum = 0;
    for(int i = 0; i < 256; i++)
    {
        float b = bright[i] / sumB;
        float d = dark[i] / sumD;
        float m = mid[i] / sumM;
 
        sum +=  (52 * b + 37 * m + 11 * d) / 100;
        target[i] = sum;
    }
 
    {
        float histMap[256];
 
        specific_hist(hist, target, histMap);
 
        for(int y = 0; y < rows; y++)
        {
            float *ptr1 = (float*)(gray.data + gray.step * y);
            float *ptr2 = (float*)(tone.data + tone.step * y);
 
            for(int x = 0; x < cols; x++)
                ptr2[x] = histMap[(int)ptr1[x]] / 255.0;
        }
 
    }
}
 
 
void texture_rendering(cv::Mat &tone, cv::Mat &image,  cv::Mat &texture)
{
    int rows = tone.rows;
    int cols = tone.cols;
 
    int ny = ceil(float(rows) / image.rows);
    int nx = ceil(float(cols) / image.cols);
 
    cv::Mat beta(rows, cols, CV_32FC1, cv::Scalar(1));
 
    assert(tone.depth() == CV_32F && image.depth() == CV_32F);
 
    texture = cv::Mat(rows, cols, CV_32FC1, cv::Scalar(0));
 
    if(ny > 1 || nx > 1)
        cv::repeat(image, ny, nx, image);
 
    cv::resize(image, image, cv::Size(cols, rows));
    image /= 255;
 
    for(int x = 0; x < cols; x++)
        beta.at<float>(0, x) = logf(tone.at<float>(0, x) + FLT_EPSILON) /
            logf(image.at<float>(0, x) + FLT_EPSILON);
 
 
    for(int y = 0; y < rows; y++)
        beta.at<float>(y, 0) = logf(tone.at<float>(y, 0) + FLT_EPSILON) /
            logf(image.at<float>(y, 0) + FLT_EPSILON);
 
 
    for(int y = 1; y < rows; y++)
    {
        for(int x = 1; x < cols; x++)
        {
            float t = logf(image.at<float>(y, x) + FLT_EPSILON);
            float v = (t * logf(tone.at<float>(y, x) + FLT_EPSILON) +
                    0.2 * ( beta.at<float>(y-1, x) + beta.at<float>(y, x - 1) )) / (t * t + 0.4);
 
            beta.at<float>(y, x) = v;
        }
    }
 
#ifdef DEBUG_INFO
    cv::imshow("beta", beta);
    cv::waitKey();
#endif
 
 
    for(int y = 0; y < rows; y++)
        for(int x = 0; x < cols; x++)
            texture.at<float>(y, x) = powf(image.at<float>(y, x), beta.at<float>(y, x));
}
 
 
void generate_sketch_images(cv::Mat &img, cv::Mat &pencilSketch, cv::Mat &colorSketch)
{
    assert(img.channels() == 3);
 
    cv::Mat gray, structure, tone, texture;
 
    cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
 
    float hist[256];
 
    calc_hist(gray.data, gray.cols, gray.rows, gray.step, hist);
 
    gray.convertTo(gray, CV_32FC1);
 
    generate_stroke_structure(gray, 1, 8, 0.9, structure);
 
#ifdef DEBUG_INFO
    cv::imshow("structure", structure);
    cv::waitKey();
#endif
 
    generate_tone(gray, hist, tone);
 
#ifdef DEBUG_INFO
    cv::imshow("tone", tone);
    cv::waitKey();
#endif
 
    texture_rendering(tone, gray, texture);
 
#ifdef DEBUG_INFO
    cv::imshow("texture", texture);
    cv::waitKey();
#endif
 
    pencilSketch = structure.mul(texture) * 255;
 
 
    pencilSketch.convertTo(pencilSketch, CV_8UC1);
#ifdef DEBUG_INFO
    cv::imshow("pencil", pencilSketch);
    cv::waitKey();
#endif
 
    {
        cv::Mat labImg;
        std::vector<cv::Mat> channels;
        cv::cvtColor(img, labImg, cv::COLOR_BGR2Lab);
        cv::split(labImg, channels);
        channels[0] = pencilSketch;
        cv::merge(channels, labImg);
        cv::cvtColor(labImg, colorSketch, cv::COLOR_Lab2BGR);
    }
#ifdef DEBUG_INFO
    cv::imshow("color", colorSketch);
    cv::waitKey();
#endif
 
}
 
 
int main(int argc, char **argv)
{
    if(argc < 4)
    {
        printf("Usage: %s [input image] [output pencil image] [output color pencil image]\n", argv[0]);
        return 1;
    }
 
    cv::Mat img = cv::imread(argv[1], CV_LOAD_IMAGE_COLOR);
    cv::Mat pencil, colorPencil;
 
    generate_sketch_images(img, pencil, colorPencil);
 
 
    cv::imshow("pencil", pencil);
    cv::imshow("color pencil", colorPencil);
    cv::waitKey();
 
    if(!cv::imwrite(argv[2], pencil))
    {
        printf("Can't write image %s\n", argv[2]);
        return 2;
    }
 
    if(! cv::imwrite(argv[3], colorPencil))
    {
        printf("Can't write image %s\n", argv[3]);
        return 3;
    }
 
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章