2012-06-21 19 views
9

मैं गणितीय छवि विश्लेषण को देखने के लिए उपयोगकर्ता इंटरफ़ेस कोड वाले किसी की सहायता कर रहा हूं। इस प्रक्रिया के दौरान हम त्रिकोणीय में 2 डी आकार का हिस्सा विभाजित करेंगे, और इनमें से कुछ त्रिकोणों को यूआई पर भर देंगे।मुझे एलियासिंग कलाकृतियों से बचने के लिए एक पिक्सेल-सही त्रिभुज भरने वाले एल्गोरिदम की आवश्यकता है

हम एक भरने वाले एल्गोरिदम की तलाश कर रहे हैं जो गारंटी देता है कि यदि दो त्रिकोण एक किनारे साझा करते हैं (विशेष रूप से, यदि त्रिभुजों के किसी भी दो कोने समान हैं), तो ड्राइंग ऑर्डर और एलियासिंग के बावजूद खाली नहीं होगा, अनचाहे दोनों के बीच की रेखा पर पिक्सेल। (यह ठीक है अगर कुछ पिक्सल दो बार खींचे जाते हैं।) परिणाम मनमाने ढंग से स्केलिंग के तहत ठीक दिखना चाहिए। कुछ त्रिभुज स्थानों में बहुत पतली slivers हो सकता है, नीचे 1 पिक्सेल चौड़ा।

आदर्श रूप से यह भी एक उचित कुशल भरने वाला एल्गोरिदम होना चाहिए!

विरोधी अलियासिंग, त्रिकोण प्रतिपादन में उपयोग नहीं किया जाएगा के रूप में अंतिम छवि 1-बिट गहराई की जरूरत है।

संदर्भ एक छवि मान्यता आवेदन है, तो सभी शीर्ष निर्देशांक एक पिक्सेल करने के लिए सही हो जाएगा।

+2

आप बस (अपने पुस्तकालय की) एक डिफ़ॉल्ट भरने प्रक्रिया के साथ त्रिकोण नहीं बना सकता है और फिर से एक भी पोस्ट-प्रोसेसिंग आपरेशन लापता पिक्सल भरता है? – emesx

+0

@ एल्म्स: यह एक स्वीकार्य दृष्टिकोण होगा, लेकिन यह अभी भी गायब पिक्सल की पहचान के लिए 'अच्छा एल्गोरिदम' छोड़ देता है। (मुझे आशा है कि जो कोई मुझे ग्राफिक्स के बारे में और जानता है, वह मुझे त्रिभुज-रास्टरराइजेशन एल्गोरिदम जानता है जो इसे पहले स्थान पर होने से रोकता है।) – Tynam

+0

ठीक है, क्या आप पृष्ठभूमि का रंग जानते हैं? यहां तक ​​कि यदि आप नहीं करते हैं, तो आप एक सरल ईरोड/पोस्ट प्रोसेसिंग को फैलाने का प्रयास कर सकते हैं। – emesx

उत्तर

16

आवश्यकताओं को देखते हुए यह लग रहा है वहाँ एक सरल उपाय है जैसे।

सबसे पहले, त्रिभुज किनारों को रास्टराइज करें। आप उस के लिए ब्रेसनहेम लाइन ड्रॉइंग एल्गोरिदम का उपयोग कर सकते हैं (जैसा कि नीचे दिए गए कोड में है) या जो कुछ भी काम करता है। फिर बीच में क्षेत्र भरें। यह मनमाने ढंग से पतले त्रिकोण के साथ काम करेगा।

यह सुनिश्चित करने के लिए कि त्रिभुजों को खींचा जाने वाले क्रम के बावजूद कोई अंतराल नहीं है और त्रिभुज-ड्राइंग कोड को प्रदान किए गए शिखर के क्रम के बावजूद आप साझा किनारों को समान रूप से साझा करने वाले त्रिकोणों में रास्टराइज़ करना चाहते हैं धार। वही तरीका हर बार एक ही पिक्सल का मतलब है।

यह सुनिश्चित करने के लिए कि हर बार जब आप वर्टेक्स निर्देशांक के समान जोड़े से एक ही पिक्सल प्राप्त करते हैं, तो आप मूल रूप से एक निश्चित क्रम स्थापित करना चाहते हैं, यानी एक ऐसा नियम स्थापित करें जो हमेशा दिए गए दो में से एक ही वर्टेक्स का चयन करेगा जिस क्रम में उन्हें दिया जाता है।

इस आदेश को लागू करने का एक आसान तरीका है आपकी लाइन (त्रिकोण किनारे) को 2-डी वेक्टर के रूप में पेश करना और इसकी दिशा को फ़्लिप करना यदि यह नकारात्मक वाई की दिशा में इंगित करता है या एक्स अक्ष के समानांतर होता है और इसमें अंक नकारात्मक एक्स की दिशा। कुछ ASCII कला के लिए समय! :)

 3 2 1 
     \ |/
     \ |/
     \|/ 
4 --------+--------- 0 
     /|\ 
     /| \ 
    /| \ 
     5 6 7 

     4 -> 0 
     5 -> 1 
     6 -> 2 
     7 -> 3 

देखें, यहाँ रेखा खंड, कहते हैं, 1 और रेखा खंड 5 वास्तव में बात यह है कि एक ही प्रकार हैं, फर्क सिर्फ इतना है अन्य समाप्ति बिंदु को मूल में अंत बिंदु से दिशा है। इसलिए हम इन मामलों को आधे से घटाकर 4 से 7 खंडों को 0 से 3 तक विभाजित करके दिशा निर्देश अस्पष्टता से छुटकारा पा सकते हैं। आइओ, हम बढ़ते एक्स की दिशा में, किनारे पर समान हैं, या तो वाई के OR को बढ़ाने की दिशा में जाना चुनते हैं।

#include <stddef.h> 
#include <limits.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <time.h> 

#define SCREEN_HEIGHT 22 
#define SCREEN_WIDTH 78 

// Simulated frame buffer 
char Screen[SCREEN_HEIGHT][SCREEN_WIDTH]; 

void SetPixel(long x, long y, char color) 
{ 
    if ((x < 0) || (x >= SCREEN_WIDTH) || 
     (y < 0) || (y >= SCREEN_HEIGHT)) 
    { 
    return; 
    } 

    if (Screen[y][x] == ' ') 
    Screen[y][x] = color; 
    else 
    Screen[y][x] = '*'; 
} 

void Visualize(void) 
{ 
    long x, y; 

    for (y = 0; y < SCREEN_HEIGHT; y++) 
    { 
    for (x = 0; x < SCREEN_WIDTH; x++) 
    { 
     printf("%c", Screen[y][x]); 
    } 

    printf("\n"); 
    } 
} 

typedef struct 
{ 
    long x, y; 
    unsigned char color; 
} Point2D; 


// min X and max X for every horizontal line within the triangle 
long ContourX[SCREEN_HEIGHT][2]; 

#define ABS(x) ((x >= 0) ? x : -x) 

// Scans a side of a triangle setting min X and max X in ContourX[][] 
// (using the Bresenham's line drawing algorithm). 
void ScanLine(long x1, long y1, long x2, long y2) 
{ 
    long sx, sy, dx1, dy1, dx2, dy2, x, y, m, n, k, cnt; 

    sx = x2 - x1; 
    sy = y2 - y1; 

/* 
     3 2 1 
     \ |/
     \ |/
     \|/ 
4 --------+--------- 0 
     /|\ 
     /| \ 
    /| \ 
     5 6 7 

     4 -> 0 
     5 -> 1 
     6 -> 2 
     7 -> 3 
*/ 
    if (sy < 0 || sy == 0 && sx < 0) 
    { 
    k = x1; x1 = x2; x2 = k; 
    k = y1; y1 = y2; y2 = k; 
    sx = -sx; 
    sy = -sy; 
    } 

    if (sx > 0) dx1 = 1; 
    else if (sx < 0) dx1 = -1; 
    else dx1 = 0; 

    if (sy > 0) dy1 = 1; 
    else if (sy < 0) dy1 = -1; 
    else dy1 = 0; 

    m = ABS(sx); 
    n = ABS(sy); 
    dx2 = dx1; 
    dy2 = 0; 

    if (m < n) 
    { 
    m = ABS(sy); 
    n = ABS(sx); 
    dx2 = 0; 
    dy2 = dy1; 
    } 

    x = x1; y = y1; 
    cnt = m + 1; 
    k = n/2; 

    while (cnt--) 
    { 
    if ((y >= 0) && (y < SCREEN_HEIGHT)) 
    { 
     if (x < ContourX[y][0]) ContourX[y][0] = x; 
     if (x > ContourX[y][1]) ContourX[y][1] = x; 
    } 

    k += n; 
    if (k < m) 
    { 
     x += dx2; 
     y += dy2; 
    } 
    else 
    { 
     k -= m; 
     x += dx1; 
     y += dy1; 
    } 
    } 
} 

void DrawTriangle(Point2D p0, Point2D p1, Point2D p2) 
{ 
    long y; 

    for (y = 0; y < SCREEN_HEIGHT; y++) 
    { 
    ContourX[y][0] = LONG_MAX; // min X 
    ContourX[y][1] = LONG_MIN; // max X 
    } 

    ScanLine(p0.x, p0.y, p1.x, p1.y); 
    ScanLine(p1.x, p1.y, p2.x, p2.y); 
    ScanLine(p2.x, p2.y, p0.x, p0.y); 

    for (y = 0; y < SCREEN_HEIGHT; y++) 
    { 
    if (ContourX[y][1] >= ContourX[y][0]) 
    { 
     long x = ContourX[y][0]; 
     long len = 1 + ContourX[y][1] - ContourX[y][0]; 

     // Can draw a horizontal line instead of individual pixels here 
     while (len--) 
     { 
     SetPixel(x++, y, p0.color); 
     } 
    } 
    } 
} 

int main(void) 
{ 
    Point2D p0, p1, p2, p3; 

    // clear the screen 
    memset(Screen, ' ', sizeof(Screen)); 

    // generate random triangle coordinates 

    srand((unsigned)time(NULL)); 

    // p0 - p1 is going to be the shared edge, 
    // make sure the triangles don't intersect 
    for (;;) 
    { 
    p0.x = rand() % SCREEN_WIDTH; 
    p0.y = rand() % SCREEN_HEIGHT; 

    p1.x = rand() % SCREEN_WIDTH; 
    p1.y = rand() % SCREEN_HEIGHT; 

    p2.x = rand() % SCREEN_WIDTH; 
    p2.y = rand() % SCREEN_HEIGHT; 

    p3.x = rand() % SCREEN_WIDTH; 
    p3.y = rand() % SCREEN_HEIGHT; 

    { 
     long vsx = p0.x - p1.x; 
     long vsy = p0.y - p1.y; 
     long v1x = p0.x - p2.x; 
     long v1y = p0.y - p2.y; 
     long v2x = p0.x - p3.x; 
     long v2y = p0.y - p3.y; 
     long z1 = vsx * v1y - v1x * vsy; 
     long z2 = vsx * v2y - v2x * vsy; 
     // break if p2 and p3 are on the opposite sides of p0-p1 
     if (z1 * z2 < 0) break; 
    } 
    } 

    printf("%ld:%ld %ld:%ld %ld:%ld %ld:%ld\n\n", 
     p0.x, p0.y, 
     p1.x, p1.y, 
     p2.x, p2.y, 
     p3.x, p3.y); 

    // draw the triangles 

    p0.color = '-'; 
    DrawTriangle(p0, p3, p1); 
    p1.color = '+'; 
    DrawTriangle(p1, p2, p0); 

    Visualize(); 

    return 0; 
} 

नमूना उत्पादन:

30:10 5:16 16:6 59:17 







       +++ 
       ++++++++ 
       ++++++++++++ 
      +++++++++++++++++ 
      +++++++++++++++****--- 
      +++++++++++++****----------- 
     ++++++++++****------------------- 
     ++++++*****---------------------------- 
     +++****------------------------------------- 
     ****--------------------------------------------- 
    *----------------------------------------------------- 
                  - 

लीजेंड:

यहाँ कैसे आप कोड में यह कर सकता है

  • "+" - त्रिकोण 1
  • के पिक्सल " - "- त्रिभुज के पिक्सेल 2
  • "*" - बढ़त के पिक्सल (वजह से त्रिकोण 1 और 2

खबरदार है कि भले ही कोई रिक्त अंतराल (पिक्सेल), त्रिकोण जिसका पिक्सल (साझा किनारे पर) अधिलेखित हो हो जाएगा के बीच साझा किया इसके शीर्ष पर खींचा गया दूसरा त्रिकोण) बहुत पतला होने पर अलग या अजीब रूप से आकार के रूप में दिखाई दे सकता है। उदाहरण:

2:20 12:8 59:15 4:17 









      *++++++ 
      *+++++++++++++ 
      *+++++++++++++++++++++ 
     -*++++++++++++++++++++++++++++ 
     -*++++++++++++++++++++++++++++++++++++ 
     *+++++++++++++++++++++++++++++++++++++++++++ 
     *+++++++++++++++++++++++++++++++++++++++++++++++++++ 
     *+++++++++++++++++++++++++++++++++++++++++++++++++++++ 
    *+++++++++++++++++++++++++++++++++++++++++++ 
    -*+++++++++++++++++++++++++++++++ 
    -*+++++++++++++++++++++ 
    *++++++++++ 
    * 
+1

+1 और सबसे सरल अवधारणाओं की पूरी तरह से व्याख्या। हम शायद इस तरह कुछ करेंगे। (क्योंकि हमारे कई त्रिभुज पतले स्लाइस होते हैं, अलग-अलग या अजीब आकार अपरिहार्य होते हैं इससे कोई फर्क नहीं पड़ता कि हम किस दृष्टिकोण का उपयोग करते हैं; यह तब तक ठीक है जब तक हमारा भरना * कुछ * उपयुक्त * अंतराल को छोड़ देता है।) – Tynam

0

क्या आप देख रहे हैं एक floodfill एल्गोरिथ्म है।

Here's one

Another link

आप अधिक के लिए 'बाढ़फिल-एल्गोरिदम' Google पर जा सकते हैं।

[संपादित करें]

शायद this site [छायांकर्ता आधारित वायरफ़्रेम आहरण] कुछ और विचारों को प्रस्तुत कर सकते हैं।

+0

सरल बीज आधारित बाढ़ भरने बाहर हैं, क्योंकि कुछ त्रिभुजों के पास 'बिंदु के नजदीक फंसे पिक्सेल' में भागने के लिए पर्याप्त तीव्र कोण होंगे । (इसके अलावा, एक इंटीरियर स्टार्ट पिक्सेल विश्वसनीय रूप से एंग्लड 'स्लिम' त्रिकोणों में एक मुद्दा हो सकता है।) हालांकि, क्विकफिल चर्चा के लिए आपका लिंक दिलचस्प है; हम एक उचित रूप ले लेंगे। – Tynam

+0

@ गति: अपूर्ण पिक्सल के लिए दिलचस्प क्षेत्रों की जांच करने के लिए पिक्सेल-स्कैनिंग तकनीक का उपयोग करना संभव हो सकता है, उदा। बहुत तीव्र कोण या पिक्सेल-चौड़े त्रिकोण: यदि अपूर्ण पिक्सेल कम से कम एक सीमा के भीतर स्थित है, तो इसे भरना चाहिए। इसका मतलब यह हो सकता है कि पूरे त्रिकोण का एक लाइन-स्कैन निष्पादित पिक्सल के लिए कर रहा है (एक मनमानी तरफ से शुरू होने वाली स्कैन लाइनें और दूसरे पक्षों के साथ अंत-बिंदुओं के साथ समानांतर चाल चलनी चाहिए)। – slashmais

0

यह सबसे कारगर नहीं है, लेकिन एक वर्ग त्रिकोण और परीक्षण युक्त यदि प्रत्येक पिक्सेल त्रिकोण के भीतर है से अधिक पाश आप कर सकते थे।

स्यूडोकोड:

for(x : minX -> maxX) 
    for(y : minY -> maxY) 
     if(triangle.contains(x,y)) 
      drawPixel(x,y); 

कहाँ MINX न्यूनतम एक्स तीन कोने के बीच है और इसी तरह Maxx, miny और Maxy के लिए समन्वय है।

एक तेज़ एल्गोरिदम के लिए आप पहले कुछ त्वरित और गंदे भरने (जैसे स्लैशमाइस बाढ़ भरना) कर सकते हैं और फिर किनारों के चारों ओर पिक्सेल के लिए ऐसा कर सकते हैं।

प्वाइंट-इन-त्रिकोण परीक्षण here वर्णन किया गया है।

2

आसन्न त्रिकोण के बारे में आपकी चिंता एक वैध है। यदि दो त्रिकोण एक किनारे साझा करते हैं, तो आप यह सुनिश्चित करना चाहते हैं कि उस किनारे के साथ प्रत्येक पिक्सेल विशेष रूप से एक त्रिकोण या दूसरे के लिए "संबंधित" है। यदि उनमें से एक पिक्सेल या तो त्रिभुज से संबंधित नहीं है, तो आपके पास एक अंतर है। यदि यह दोनों त्रिकोणों से संबंधित है, तो आपने ओवरड्रा (जो अक्षम है) है और रंग त्रिभुज के क्रम पर निर्भर हो सकता है (जो निर्धारित नहीं हो सकता है)।

चूंकि आप एंटी-एलियासिंग का उपयोग नहीं कर रहे हैं, यह वास्तव में बहुत कठिन नहीं है। यह एक स्मार्ट एल्गोरिदम नहीं है जिसे आपको सावधानीपूर्वक कार्यान्वयन के रूप में चाहिए।

त्रिभुज को रास्टराइज़ करने का सामान्य तरीका क्षैतिज खंडों की गणना करना है जो त्रिकोण से ऊपर से नीचे तक हैं।आप वर्तमान बाएं और दाएं किनारों का ट्रैक रखकर ऐसा करते हैं, और अनिवार्य रूप से प्रत्येक स्कैनलाइन पर प्रत्येक किनारे के लिए एक्स-इंटरसेप्ट गणना करते हैं। यह एक साथ चल रहे दो ब्रेसेनहेम-स्टाइल लाइन-ड्राइंग एल्गोरिदम के साथ भी किया जा सकता है। प्रभावी रूप से, रास्टरराइजेशन एक ऐसे फ़ंक्शन पर कई कॉलों की मात्रा है जो कुछ बाएं समन्वय x0 से कुछ स्कैनलाइन x1 पर कुछ स्कैनलाइन y पर एक क्षैतिज रेखा खंड खींचता है।

void DrawHLine(int y, int x0, int x1); 

आमतौर पर क्या किया है ताकि एक्स निर्देशांक लगातार कि क्या वे एक त्रिकोण के दाएं किनारे का हिस्सा हैं की परवाह किए बिना गणना यकीन है कि बंद एक सुसंगत तरीके से एक्स-अवरोध रास्टेराइज़र दौर बनाने के लिए, है या आसन्न त्रिभुज के बाएं किनारे। यह गारंटी देता है कि साझा किनारे के साथ प्रत्येक पिक्सेल दोनों त्रिभुजों से संबंधित होगा।

हम DrawHLine अदल-बदल करके डबल-स्वामित्व को हल इतना है कि यह x1अनन्य अप करने के लिए x0 समावेशी से पिक्सल भरता है। तो साझा किनारे पर उन सभी दोगुनी स्वामित्व वाली पिक्सेल को साझा किनारे के दाईं ओर त्रिकोण से संबंधित किया जाता है।

+0

+1। मैंने अपने जवाब में काफी कुछ किया। व्यापक ASCII के लिए –

संबंधित मुद्दे