선을 사용하여 이미지 재생


31

트루 컬러 RGB 이미지 I , L 을 그릴 최대 라인 수 및 각 라인 의 최소 m 및 최대 M 길이 를 취하는 프로그램을 작성하십시오 . 가능한 한 I 처럼 보이고 L 이하의 직선을 사용하여 그려지 는 이미지 O 를 출력합니다 . 모두 직선 길이가 mM 사이 입니다.

각 선은 단색이어야 하고 O 경계에 두 끝 점이 있어야하며 Bresenham의 선 알고리즘을 사용하여 그려야 합니다 (대부분의 그래픽 라이브러리에서 이미 사용함 ). 개별 선의 두께는 1 픽셀 만 가능합니다.

길이가 0 인 모든 라인은 하나 이상의 픽셀을 차지해야합니다. 서로의 위에 선을 그릴 수 있습니다.

선을 그리기 전에 O 의 배경을 단색 ( I에 따라 다름)으로 초기화 할 수 있습니다 .

세부

  • O는 같은 크기를 가져야한다 I .
  • L 은 항상 음이 아닌 정수입니다. I 의 면적보다 클 수 있습니다 .
  • mMM > = m 인 음이 아닌 부동 소수점 수입니다 . 두 픽셀 사이의 거리는 중심 간의 유클리드 거리입니다. 이 거리가 m 보다 작거나 M 보다 크면 해당 픽셀 사이의 선이 허용되지 않습니다.
  • 라인을 앤티 앨리어싱해서는 안됩니다.
  • 불투명도와 알파를 사용해서는 안됩니다.
  • 백만 픽셀 미만 과 L 미만의 이미지 에서 괜찮은 현대 컴퓨터 에서 프로그램을 실행하는 데 1 시간 이상 걸리지 않아야합니다 .

테스트 이미지

당신은 물론 우리에게 (내가 때 발생할 것으로 예상 가장 정확하거나 흥미 출력 이미지를 표시해야합니다 L은 5 %와 픽셀 수의 25 % 사이에 I , 및 mM은 주변의 대각선 크기의 10 분이다)를.

다음은 몇 가지 테스트 이미지입니다 (원본을 보려면 클릭하십시오). 당신은 또한 자신의 게시물을 게시 할 수 있습니다.

모나리자 폭포 나이트 호크 별이 빛나는 밤 골든 게이트 브릿지

더 간단한 이미지 :

펜로즈 계단뫼비우스의 띠 힐버트 커브

이것은 인기 콘테스트입니다. 가장 높은 투표 제출이 승리합니다.

노트

  • L을 절대 값뿐만 아니라 I 의 총 픽셀의 백분율에서 파생시키는 것이 도움이 될 수 있습니다 . 예를 들면 >>> imageliner I=img.png L=50% m=10 M=20같은 일이 될 것입니다 >>> imageliner I=img.png L=32 m=10 M=20경우 img.png8 8 픽셀 이미지했다. mM에 대해 비슷한 것을 할 수 있습니다 . 필요하지 않습니다.
  • 선이 경계를 벗어날 수 없으므로 가능한 가장 긴 선은 대각선 길이 I 입니다. 데 M을 , 이것보다 높은 것은 비록 아무것도 중단해서는 안된다.
  • 당연히, m 이 0이고 LI 의 픽셀 수보다 크거나 같은 경우 , O 는 각각의 픽셀 위치에서 길이 0 "라인"을 가짐으로써 I와 동일 할 수있다 . 이 동작은 필요하지 않습니다.
  • 틀림없이, I 의 형태를 재현하는 것이 색상을 재현하는 것보다 중요합니다. 가장자리 감지 를 원할 수 있습니다 .

명확히하기 : SimpleCV 와 같은 라이브러리가 허용됩니까? 그리고 대답은 m = 0 및 L = 면적을 포함하여 I, L, m 및 M에 대한 선택을 할 수 있습니까?
rationalis

@epicwisdom 네, 모든 라이브러리 (이미 구체적으로이 작업을 수행하는 것 제외)가 허용됩니다. 키포인트, 에지 감지 등을 자유롭게 사용하십시오. 귀하의 알고리즘을 위해 일해야 어떤 유효한 선택 I , L , M , M , 포함 m = 0, L = 지역. (비록 물론, 알고리즘은 매개 변수의 특정 조율에 더 보일 수 있습니다.)
칼빈의 취미

예를 들어, 이 특정 라이브러리 알고리즘 은 잘못된 대답으로 간주됩니까?
rationalis

@epicwisdom 사실 나는 그와 다른 유사한 것들을 허용 할 것입니다. 그것은 당신에게 줄에서 이미지를 만들기 위해 여전히 영리한 조정이 필요한 것처럼 보입니다.
Calvin 's Hobbies

1
선의 두께는 1이어야합니까?
aditsu

답변:


21

C ++-다소 임의의 줄과 일부

먼저 임의의 라인

알고리즘의 첫 번째 단계는 무작위로 라인을 생성하고 대상 이미지에 대해 픽셀의 평균을 취한 다음 새 라인을 페인트하면 모든 픽셀의 합한 RGB 공간 거리의 제곱이 더 작은 지 계산합니다. 있는 경우에만 페인트하십시오). 이를위한 새로운 라인 색상은 -15 / + 15의 랜덤 덧셈과 함께 RGB 값의 채널 별 평균으로 선택됩니다.

구현에 주목하고 영향을 준 것들 :

  • 초기 색상은 전체 이미지의 평균입니다. 이것은 흰색으로 만들 때와 같은 재미있는 효과를 방지하기위한 것이며, 영역이 검은 색이면 이미 흰색보다 검은 색에 가까워 밝은 녹색 선으로 표시됩니다.
  • 라인의 순수한 평균 색상을 사용하는 것은 나중 라인으로 덮어 써서 하이라이트를 생성 할 수없는 것으로 판명되지는 않습니다. 약간의 임의 편차를 수행하면 약간 도움이되지만 별이 빛나는 밤을 보면 많은 장소에서 로컬 대비가 높으면 실패합니다.

나는 몇 가지 숫자를 실험하고, 선택하고 L=0.3*pixel_count(I)왼쪽 m=10M=50. 그것은 주변에 시작하는 좋은 결과를 얻을 수 0.25에 대한 0.26행의 수에 대한,하지만 난 정확한 세부 사항에 대한 더 많은 공간을 가지고 0.3를 선택했다.

전체 크기의 골든 게이트 이미지의 경우 페인트 할 235929 개의 선이 생겼습니다 (여기서 13 초가 걸렸습니다). 여기에있는 모든 이미지는 축소 된 크기로 표시되며 전체 해상도를 보려면 새 탭에서 열거 나 다운로드해야합니다.

합당 치 않은 사람을 지워 라

다음 단계는 다소 비싸지 만 (235k 라인의 경우 약 1 시간이 걸렸지 만 "1 메가 픽셀에서 10k 라인의 시간"시간 요구 사항 내에 있어야 함) 조금 놀랍습니다. 이전에 페인트 한 선을 모두 통과하고 이미지를 개선하지 않는 선을 제거합니다. 이로 인해 다음 이미지를 생성하는 97347 줄만이 실행됩니다.

대부분의 차이점을 파악하려면 적절한 이미지 뷰어에서 이미지를 다운로드하여 비교해야합니다.

다시 시작

이제 다시 칠할 수있는 선이 많아서 총 235929를 다시 가질 수 있습니다. 할 말이 많지 않으므로 이미지는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

짧은 분석

전체 절차는 로컬 대비 및 객체 크기에 민감한 흐림 필터처럼 작동하는 것 같습니다. 그러나 선이 그려지는 위치를 확인하는 것도 흥미 롭기 때문에 프로그램에서도이 선을 기록합니다 (각 선의 경우 픽셀 색상이 한 단계 더 희미 해져 결국에는 대비가 최대화됩니다). 위의 세 가지 색상에 해당하는 것이 있습니다.

애니메이션

그리고 우리 모두 애니메이션을 좋아하기 때문에 작은 골든 게이트 이미지에 대한 전체 프로세스의 애니메이션 GIF가 있습니다. gif 형식으로 인해 디더링이 현저하게 발생합니다 (트루 컬러 애니메이션 파일 형식 및 브라우저 제조업체의 제작자가 자아와 전쟁을 벌이고 있기 때문에 트루 컬러 애니메이션에 대한 표준 형식이 없습니다. ).

좀 더

요청에 따라 다른 이미지의 결과는 다음과 같습니다 (다시 축소하지 않으려면 새 탭에서 이미지를 열어야 할 수도 있습니다)

미래 생각

코드를 가지고 놀면 약간의 변형이 생길 수 있습니다.

  • 평균을 기준으로하지 않고 무작위로 선의 색상을 선택하십시오. 두 번 이상의주기가 필요할 수 있습니다.
  • pastebin의 코드에는 유전자 알고리즘에 대한 아이디어가 포함되어 있지만 이미지는 이미 너무 좋아서 너무 많은 세대가 걸릴 수 있으며이 코드는 너무 느려서 "1 시간"규칙에 맞지 않습니다.
  • 또 다른 라운드의 지우기 / 다시 그리기를 수행하십시오.
  • 선을 지울 수있는 위치의 제한을 변경하십시오 (예 : "최소 N 이미지를 향상시켜야합니다")

코드

이것들은 두 가지 주요 유용한 기능입니다. 전체 코드는 여기에 맞지 않으며 http://ideone.com/Z2P6Ls

bmp클래스 rawraw_line 함수는 형식을 BMP에 기록 할 수있는 객체에 각각 접근 픽셀 라인을 (그것은 단지 몇 가지 해킹 주위에 거짓말을하고 있었고, 난 그 어떤 라이브러리에서이 다소 무관하게 생각했다).

입력 파일 형식은 PPM입니다

std::pair<bmp,std::vector<line>>  paint_useful( const bmp& orig, bmp& clone, std::vector<line>& retlines, bmp& layer, const std::string& outprefix, size_t x, size_t y )
{
        const size_t pixels = (x*y);
        const size_t lines = 0.3*pixels;
//      const size_t lines = 10000;

//      const size_t start_accurate_color = lines/4;

        std::random_device rnd;

        std::uniform_int_distribution<size_t> distx(0,x-1);
        std::uniform_int_distribution<size_t> disty(0,y-1);
        std::uniform_int_distribution<size_t> col(-15,15);
        std::uniform_int_distribution<size_t> acol(0,255);

        const ssize_t m = 1*1;
        const ssize_t M = 50*50;

        retlines.reserve( lines );

        for (size_t i = retlines.size(); i < lines; ++i)
        {
                size_t x0;
                size_t x1;

                size_t y0;
                size_t y1;

                size_t dist = 0;
                do
                {
                        x0 = distx(rnd);
                        x1 = distx(rnd);

                        y0 = disty(rnd);
                        y1 = disty(rnd);

                        dist = distance(x0,x1,y0,y1);
                }
                while( dist > M || dist < m );

                std::vector<std::pair<int32_t,int32_t>> points = clone.raw_line_pixels(x0,y0,x1,y1);

                ssize_t r = 0;
                ssize_t g = 0;
                ssize_t b = 0;

                for (size_t i = 0; i < points.size(); ++i)
                {
                        r += orig.raw(points[i].first,points[i].second).r;
                        g += orig.raw(points[i].first,points[i].second).g;
                        b += orig.raw(points[i].first,points[i].second).b;
                }

                r += col(rnd);
                g += col(rnd);
                b += col(rnd);

                r /= points.size();
                g /= points.size();
                b /= points.size();

                r %= 255;
                g %= 255;
                b %= 255;

                r = std::max(ssize_t(0),r);
                g = std::max(ssize_t(0),g);
                b = std::max(ssize_t(0),b);

//              r = acol(rnd);
//              g = acol(rnd);
//              b = acol(rnd);

//              if( i > start_accurate_color )
                {
                        ssize_t dp = 0; // accumulated distance of new color to original
                        ssize_t dn = 0; // accumulated distance of current reproduced to original
                        for (size_t i = 0; i < points.size(); ++i)
                        {
                                dp += rgb_distance(
                                                                                orig.raw(points[i].first,points[i].second).r,r,
                                                                                orig.raw(points[i].first,points[i].second).g,g,
                                                                                orig.raw(points[i].first,points[i].second).b,b
                                                                        );

                                dn += rgb_distance(
                                                                                clone.raw(points[i].first,points[i].second).r,orig.raw(points[i].first,points[i].second).r,
                                                                                clone.raw(points[i].first,points[i].second).g,orig.raw(points[i].first,points[i].second).g,
                                                                                clone.raw(points[i].first,points[i].second).b,orig.raw(points[i].first,points[i].second).b
                                                                        );

                        }

                        if( dp > dn ) // the distance to original is bigger, use the new one
                        {
                                --i;
                                continue;
                        }
                        // also abandon if already too bad
//                      if( dp > 100000 )
//                      {
//                              --i;
//                              continue;
//                      }
                }

                layer.raw_line_add(x0,y0,x1,y1,{1u,1u,1u});
                clone.raw_line(x0,y0,x1,y1,{(uint32_t)r,(uint32_t)g,(uint32_t)b});
                retlines.push_back({ (int)x0,(int)y0,(int)x1,(int)y1,(int)r,(int)g,(int)b});

                static time_t last = 0;
                time_t now = time(0);
                if( i % (lines/100) == 0 )
                {
                        std::ostringstream fn;
                        fn << outprefix + "perc_" << std::setw(3) << std::setfill('0') << (i/(lines/100)) << ".bmp"; 
                        clone.write(fn.str());
                        bmp lc(layer);
                        lc.max_contrast_all();
                        lc.write(outprefix + "layer_" + fn.str());
                }

                if( (now-last) > 10 )
                {
                        last = now;
                        static int st = 0;
                        std::ostringstream fn;
                        fn << outprefix + "inter_" << std::setw(8) << std::setfill('0') << i << ".bmp";
                        clone.write(fn.str());

                        ++st;
                }
        }
        clone.write(outprefix + "clone.bmp");
        return { clone, retlines };
}


void erase_bad( std::vector<line>& lines, const bmp& orig )
{
        ssize_t current_score = evaluate(lines,orig);

        std::vector<line> newlines(lines);

        uint32_t deactivated = 0;
        std::cout << "current_score = " << current_score << "\n";
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                newlines[i].active = false;
                ssize_t score = evaluate(newlines,orig);
                if( score > current_score )
                {
                        newlines[i].active = true;
                }
                else
                {
                        current_score = score;
                        ++deactivated;
                }
                if( i % 1000 == 0 )
                {
                        std::ostringstream fn;
                        fn << "erase_" << std::setw(6) << std::setfill('0') << i << ".bmp";
                        bmp tmp(orig);
                        paint(newlines,tmp);
                        tmp.write(fn.str());
                        paint_layers(newlines,tmp);
                        tmp.max_contrast_all();
                        tmp.write("layers_" + fn.str());
                        std::cout << "\r i = " << i << std::flush;
                }
        }
        std::cout << "\n";
        std::cout << "current_score = " << current_score << "\n";
        std::cout << "deactivated = " << deactivated << "\n";


        bmp tmp(orig);

        paint(newlines,tmp);
        tmp.write("newlines.bmp");
        lines.clear();
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                if( newlines[i].is_active() )
                {
                        lines.push_back(newlines[i]);
                }
        }
}

+1, 정말 좋습니다. 다른 테스트 이미지에 대한 결과가 있습니까?
Nathaniel

1
@Nathaniel : 나는 몇 가지를 추가했습니다. "간단한"이미지는 레크리에이션이 거의 완벽한 픽셀이기 때문에 방해가되지 않습니다.
PlasmaHH

17

자바-임의의 줄

임의의 선을 그리고 소스 영상 평균 색상을 계산하는 매우 기본적인 솔루션입니다. 배경색이 소스 평균 색으로 설정됩니다.

L = 5000, m = 10, M = 50

여기에 이미지 설명을 입력하십시오

L = 10000, m = 10, M = 50

여기에 이미지 설명을 입력하십시오

편집하다

나는 라인의 인구를 처리하는 유전자 알고리즘을 추가했습니다. 각 세대마다 최고 50 % 라인 만 유지하고 다른 라인은 버리고 무작위로 새로운 라인을 생성합니다. 라인을 유지하기위한 기준은 다음과 같습니다.

  • 소스 사진 색상까지의 거리가 작습니다
  • 다른 선과의 교차 수 (작을수록 좋음)
  • 그들의 길이 (더 길수록 좋습니다)
  • 가장 가까운 이웃과의 각도 (작을수록 좋음)

실망스럽게도, 알고리즘은 실제로 화질을 향상시키지 않는 것 같습니다 :-( 선이 점점 더 평행 해지고 있습니다.

1 세대 (5000 라인)

여기에 이미지 설명을 입력하십시오

10 세대 (5000 라인)

여기에 이미지 설명을 입력하십시오

파라미터로 연주

여기에 이미지 설명을 입력하십시오여기에 이미지 설명을 입력하십시오여기에 이미지 설명을 입력하십시오

package line;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import snake.Image;

public class Lines {

    private final static int NB_LINES = 5000;
    private final static int MIN_LENGTH = 10;
    private final static int MAX_LENGTH = 50;

    public static void main(String[] args) throws IOException {     
        BufferedImage src = ImageIO.read(Image.class.getClassLoader().getResourceAsStream("joconde.png"));
        BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);


        int [] bgColor = {0, 0, 0};
        int avgRed = 0, avgGreen = 0, avgBlue = 0, count = 0;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                int colsrc = src.getRGB(x, y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
        }

        bgColor[0] = avgBlue/count; bgColor[1] = avgGreen/count; bgColor[2] = avgRed/count;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                dest.getRaster().setPixel(x, y, bgColor);
            }
        }
        List<List<Point>> lines = new ArrayList<List<Point>>();
        Random rand = new Random();
        for (int i = 0; i < NB_LINES; i++) {
            int length = rand.nextInt(MAX_LENGTH - MIN_LENGTH) + MIN_LENGTH;
            double ang = rand.nextDouble() * Math.PI;
            int lx = (int)(Math.cos(ang) * length); // can be negative or positive
            int ly = (int)(Math.sin(ang) * length); // positive only
            int sx = rand.nextInt(dest.getWidth() -1 - Math.abs(lx));
            int sy = rand.nextInt(dest.getHeight() - 1- Math.abs(ly));
            List<Point> line;
            if (lx > 0) {
                line = line(sx, sy, sx+lx, sy+ly);
            } else {
                line = line(sx+Math.abs(lx), sy, sx, sy+ly);
            }
            lines.add(line);    
        }

        // render the picture
        int [] color = {0, 0, 0};
        for (List<Point> line : lines) {

            avgRed = 0; avgGreen = 0; avgBlue = 0;
            count = 0;
            for (Point p : line) {
                int colsrc = src.getRGB(p.x, p.y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
            avgRed /= count; avgGreen /= count; avgBlue /= count;
            color[0] = avgBlue; color[1] = avgGreen; color[2] = avgRed;
            for (Point p : line) {
                dest.getRaster().setPixel(p.x, p.y, color);
            }

        }
        ImageIO.write(dest, "png", new File("a0.png"));

    }

    private static List<Point> line(int x0, int y0, int x1, int y1) {
        List<Point> points = new ArrayList<Point>();
        int deltax = x1 - x0;
        int deltay = y1 - y0;
        int tmp;
        double error = 0;       
        double deltaerr = 0;
        if (Math.abs(deltax) >= Math.abs(deltay)) {
            if (x0 > x1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltay) / deltax); 
            int y = y0;
            for (int x = x0; x <= x1; x++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (y0 < y1) y++; else y--;
                    error -= 1.0;
                }
            }
        } else {
            if (y0 > y1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltax) / deltay);   // Assume deltay != 0 (line is not horizontal),
            int x = x0;
            for (int y = y0; y <= y1; y++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (x0 < x1) x++; else x--;
                    error -= 1.0;
                }
            }
        }
        return points;
    }
}

마침내 누군가가 대답했습니다 : D 더 많은 예를보고 싶습니다.
Calvin 's Hobbies

@ 캘빈 슈어. 지금 나는 라인의 모집단을 유지하고 20 % 더 나쁜 것을 제거하고 새로운 것을 재생성함으로써 알고리즘을 개선하기 위해 노력하고 있습니다. (어떤 종류의 유전자 알고리즘)
Arnaud

나는 그런 마음을 가지고 있었지만 그것을 쓸 시간이 없었습니다. 유전자 알고리즘을 기대합니다. 결과 :)
aditsu

더 작은 각도 기준을 제거하고 싶습니까? 왜 넣었 어? 선의 교차 각이 작지만 원본 이미지가 좋아 보입니다.
justhalf

@justhalf 완료. 페인터 브러시를 시뮬레이션하기 위해 각도 기준을 추가했습니다.
Arnaud

9

C-직선

ppm 파일에서 작동하는 C의 기본 접근 방식입니다. 알고리즘은 모든 픽셀을 채우기 위해 최적의 선 길이로 세로 선을 배치하려고합니다. 배경색과 선색은 원본 이미지의 평균값 (각 색상 채널의 중앙값)으로 계산됩니다.

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define SIGN(x) ((x > 0) ? 1 : (x < 0) ? -1 : 0)
#define MIN(x, y) ((x > y) ? y : x)
#define MAX(x, y) ((x > y) ? x : y)

typedef struct {
    size_t width;
    size_t height;

    unsigned char *r;
    unsigned char *g;
    unsigned char *b;
} image;

typedef struct {
    unsigned char r;
    unsigned char g;
    unsigned char b;
} color;

void init_image(image *data, size_t width, size_t height) {
    data->width = width;
    data->height = height;
    data->r = malloc(sizeof(data->r) * data->width * data->height);
    data->g = malloc(sizeof(data->g) * data->width * data->height);
    data->b = malloc(sizeof(data->b) * data->width * data->height);
}

#define BUFFER_LEN 1024
int load_image(const char *filename, image* data) {
    FILE *f = fopen(filename, "r");
    char buffer[BUFFER_LEN];          /* read buffer */
    size_t max_value;
    size_t i;
    fgets(buffer, BUFFER_LEN, f);
    if (strncmp(buffer, "P3", 2) != 0) {
        printf("File begins with %s instead of P3\n", buffer);
        return 0;
    }

    fscanf(f, "%u", &data->width);
    fscanf(f, "%u", &data->height);
    fscanf(f, "%u", &max_value);
    assert(max_value==255);

    init_image(data, data->width, data->height);

    for (i = 0; i < data->width * data->height; i++) {
        fscanf(f, "%hhu", &(data->r[i]));
        fscanf(f, "%hhu", &(data->g[i]));
        fscanf(f, "%hhu", &(data->b[i]));
    }
    fclose(f);

    printf("Read %zux%zu pixels from %s.\n", data->width, data->height, filename);
}

int write_image(const char *filename, image *data) {
    FILE *f = fopen(filename, "w");
    size_t i;
    fprintf(f, "P3\n%zu %zu\n255\n", data->width, data->height);
    for (i = 0; i < data->width * data->height; i++) {
        fprintf(f, "%hhu %hhu %hhu ", data->r[i], data->g[i], data->b[i]);
    }
    fclose(f);
}

unsigned char average(unsigned char *data, size_t data_len) {
    size_t i;
    size_t j;
    size_t hist[256];

    for (i = 0; i < 256; i++) hist[i] = 0;
    for (i = 0; i < data_len; i++) hist[data[i]]++;
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist[i];
        if (j >= data_len / 2) return i;
    }
    return 255;
}

void set_pixel(image *data, size_t x, size_t y, unsigned char r, unsigned char g, unsigned char b) {
    data->r[x + data->width * y] = r;
    data->g[x + data->width * y] = g;
    data->b[x + data->width * y] = b;
}

color get_pixel(image *data, size_t x, size_t y) {
    color ret;
    ret.r = data->r[x + data->width * y];
    ret.g = data->g[x + data->width * y];
    ret.b = data->b[x + data->width * y];
    return ret;
}

void fill(image *data, unsigned char r, unsigned char g, unsigned char b) {
    size_t i;
    for (i = 0; i < data->width * data->height; i++) {
        data->r[i] = r;
        data->g[i] = g;
        data->b[i] = b;
    }
}

void line(image *data, size_t x1, size_t y1, size_t x2, size_t y2, unsigned char r, unsigned char g, unsigned char b) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    set_pixel(data, x, y, r, g, b);

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        set_pixel(data, x, y, r, g, b);
    }
}

color average_line(image *data, size_t x1, size_t y1, size_t x2, size_t y2) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;
    color ret;
    color px;
    size_t i;
    size_t j;
    size_t hist_r[256];
    size_t hist_g[256];
    size_t hist_b[256];
    size_t data_len = 0;

    for (i = 0; i < 256; i++) {
        hist_r[i] = 0;
        hist_g[i] = 0;
        hist_b[i] = 0;
    }

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    px = get_pixel(data, x, y);
    hist_r[px.r]++;
    hist_g[px.g]++;
    hist_b[px.b]++;
    data_len++;

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        px = get_pixel(data, x, y);
        hist_r[px.r]++;
        hist_g[px.g]++;
        hist_b[px.b]++;
        data_len++;
    }

    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_r[i];
        if (j >= data_len / 2) {
            ret.r = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_g[i];
        if (j >= data_len / 2) {
            ret.g = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_b[i];
        if (j >= data_len / 2) {
            ret.b = i;
            break;
        }
    }
    return ret;
}


void lines(image *source, image *dest, size_t L, float m, float M) {
    size_t i, j;
    float dx;
    float mx, my;
    float mm = MAX(MIN(source->width * source->height / L, M), m);
    unsigned char av_r = average(source->r, source->width * source->height);
    unsigned char av_g = average(source->g, source->width * source->height);
    unsigned char av_b = average(source->b, source->width * source->height);
    fill(dest, av_r, av_g, av_b);
    dx = (float)source->width / L;
    mx = 0;
    my = mm / 2;
    for (i = 0; i < L; i++) {
        color avg;
        mx += dx;
        my += (source->height - mm) / 8;
        if (my + mm / 2 > source->height) {
            my = mm / 2 + ((size_t)(my + mm / 2) % (size_t)(source->height - mm));
        }
        avg = average_line(source, mx, my - mm / 2, mx, my + mm / 2);
        line(dest, mx, my - mm / 2, mx, my + mm / 2, avg.r, avg.g, avg.b);
    }
}

int main(int argc, char *argv[]) {
    image source;
    image dest;
    size_t L;
    float m;
    float M;

    load_image(argv[1], &source);
    L = atol(argv[2]);
    m = atof(argv[3]);
    M = atof(argv[4]);

    init_image(&dest, source.width, source.height);
    lines(&source, &dest, L, m, M);


    write_image(argv[5], &dest);
}

L = 5000, m = 10, M = 50

L = 5000, m = 10, M = 50

L = 5000, m = 10, M = 50

L = 5000, m = 10, M = 50

L = 100000, m = 10, M = 50

여기에 이미지 설명을 입력하십시오


6

파이썬 3은 "무언가 임의의 라인과 일부", 그리고 소벨 에지 감지 기능을 기반으로합니다.

코드는 이론적으로 영원히 실행될 수 있지만 재미를 위해 밤새 실행할 수 있지만 진행 상황을 기록하므로 모든 이미지는 1-10 분 표시에서 가져옵니다.

먼저 이미지를 읽은 다음 sobel 가장자리 감지를 사용하여 모든 가장자리의 각도를 찾아서 선이 다른 색상으로 넘어 가지 않도록합니다. (lengthmin, lengthmax) 내에 임의 길이의 선이 설정되면 전체 이미지에 기여하는지 테스트합니다. 작은 선이 더 좋지만 선 길이를 10-50으로 설정했습니다.

from random import randint, uniform
import json
from PIL import Image, ImageDraw, ImageFilter
import math
k=(-1,0,1,-2,0,2,-1,0,1)
k1=(-1,-2,-1,0,0,0,1,2,1)
population=[]
lengthmin=10
lengthmax=50
number_lines=10**8
im=Image.open('0.png')
[x1,y1]=im.size
dx=0
class drawer():
    def __init__(self,genome,score,filename):
        self.genome=genome
        self.score=score
        self.filename=filename
    def initpoint(self,g1):
        g2=self.genome
        im=Image.open('0.png')
        im1=im.filter(ImageFilter.Kernel((3,3),k,1,128))
        im2=im.filter(ImageFilter.Kernel((3,3),k1,1,128))
        im1=im1.filter(ImageFilter.GaussianBlur(radius=4))
        im2=im2.filter(ImageFilter.GaussianBlur(radius=4))
        for x in range(0,number_lines):
            if(x%10**4==0):
                print(x*100/number_lines)
                self.save()
                g1.save('1.png')
            (x,y)=(randint(0,x1-1),randint(0,y1-1))
            w=im1.getpixel((x,y))[0]-128
            z=im2.getpixel((x,y))[0]-128
            w=int(w)
            z=int(z)
            W=(w**2+z**2)**0.5
            if(W!=0):
                w=(w/W)*randint(lengthmin,lengthmax)
                z=(z/W)*randint(lengthmin,lengthmax)
                (w,z)=(z,w)
                (a,b)=(x+w,y+z)
                a=int(a)
                b=int(b)
                x=int(x)
                y=int(y)
                if(a>=x1):
                    a=x1-1
                if(b>=y1):
                    b=y1-1
                if(a<0):
                    a=0
                if(b<0):
                    b=0
                if(x>=x1):
                    x=x1-1
                if(y>=y1):
                    y=y1-1
                if(x<0):
                    x=0
                if(y<0):
                    y=0
                C=[0,0,0]
                D=0
                E=0
                F=0
                G=0
                W=((x-a)**2+(y-b)**2)**0.5
                if(W!=0):
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        C[0]+=im.getpixel((c,d))[0]
                        C[1]+=im.getpixel((c,d))[1]
                        C[2]+=im.getpixel((c,d))[2]
                    C[0]/=W
                    C[1]/=W
                    C[2]/=W
                    C[0]=int(C[0])
                    C[1]=int(C[1])
                    C[2]=int(C[2])
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        E=0
                        D=0
                        D+=(g1.getpixel((c,d))[0]-im.getpixel((c,d))[0])**2
                        D+=(g1.getpixel((c,d))[1]-im.getpixel((c,d))[1])**2
                        D+=(g1.getpixel((c,d))[2]-im.getpixel((c,d))[2])**2
                        F+=D**0.5
                        E+=(im.getpixel((c,d))[0]-C[0])**2
                        E+=(im.getpixel((c,d))[1]-C[1])**2
                        E+=(im.getpixel((c,d))[2]-C[2])**2
                        G+=E**0.5
                    #print((G/W,F/W))
                    if(G<F):
                        for Z in range(0,int(W)):
                            w=(Z/W)
                            (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                            c=int(c)
                            d=int(d)
                            g1.putpixel((c,d),(int(C[0]),int(C[1]),int(C[2])))
                        g2.append((x,y,a,b,int(C[0]%256),int(C[1]%256),int(C[2]%256)))
        return(g1)
    def import_file(self):
        with open(self.filename, 'r') as infile:
            self.genome=json.loads(infile.read())
        print(len(self.genome))
    def save(self):
        with open(self.filename, 'w') as outfile:
            data = json.dumps(self.genome)
            outfile.write(data)
population.append(drawer([],0,'0.txt'))
G=0
g1=Image.new('RGB',(x1,y1),'black')
g1=population[0].initpoint(g1)
g1.save('1.png')

미국 고딕

에셔

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.