2016-01-14 11 views
26

मैंने निम्नलिखित आकार बदलने वाले एल्गोरिदम लिखा है जो छवि को ऊपर या नीचे सही ढंग से स्केल कर सकता है। यह बहुत धीमी है हालांकि प्रत्येक पाश पर वजन की सरणी के माध्यम से आंतरिक पुनरावृत्ति के कारण।स्प्लिट आकार दो एल्गोरिदम को दो पासों में विभाजित करें

मुझे काफी यकीन है कि मैं एल्गोरिदम को दो पासों में विभाजित करने में सक्षम होना चाहिए जैसा कि आप दो पास गॉसियन ब्लर के साथ करेंगे जो परिचालन जटिलता को काफी कम करेगा और प्रदर्शन को तेज करेगा। दुर्भाग्य से मैं इसे काम नहीं कर सकता। क्या कोई मदद करने में सक्षम होगा?

Parallel.For(
    startY, 
    endY, 
    y => 
    { 
     if (y >= targetY && y < targetBottom) 
     { 
      Weight[] verticalValues = this.verticalWeights[y].Values; 

      for (int x = startX; x < endX; x++) 
      { 
       Weight[] horizontalValues = this.horizontalWeights[x].Values; 

       // Destination color components 
       Color destination = new Color(); 

       // This is where there is too much operation complexity. 
       foreach (Weight yw in verticalValues) 
       { 
        int originY = yw.Index; 

        foreach (Weight xw in horizontalValues) 
        { 
         int originX = xw.Index; 
         Color sourceColor = Color.Expand(source[originX, originY]); 
         float weight = yw.Value * xw.Value; 
         destination += sourceColor * weight; 
        } 
       } 

       destination = Color.Compress(destination); 
       target[x, y] = destination; 
      } 
     } 
    }); 

वजन और सूचकांक निम्नानुसार गणना की जाती है। प्रत्येक आयाम के लिए एक:

/// <summary> 
/// Computes the weights to apply at each pixel when resizing. 
/// </summary> 
/// <param name="destinationSize">The destination section size.</param> 
/// <param name="sourceSize">The source section size.</param> 
/// <returns> 
/// The <see cref="T:Weights[]"/>. 
/// </returns> 
private Weights[] PrecomputeWeights(int destinationSize, int sourceSize) 
{ 
    IResampler sampler = this.Sampler; 
    float ratio = sourceSize/(float)destinationSize; 
    float scale = ratio; 

    // When shrinking, broaden the effective kernel support so that we still 
    // visit every source pixel. 
    if (scale < 1) 
    { 
     scale = 1; 
    } 

    float scaledRadius = (float)Math.Ceiling(scale * sampler.Radius); 
    Weights[] result = new Weights[destinationSize]; 

    // Make the weights slices, one source for each column or row. 
    Parallel.For(
     0, 
     destinationSize, 
     i => 
      { 
       float center = ((i + .5f) * ratio) - 0.5f; 
       int start = (int)Math.Ceiling(center - scaledRadius); 

       if (start < 0) 
       { 
        start = 0; 
       } 

       int end = (int)Math.Floor(center + scaledRadius); 

       if (end > sourceSize) 
       { 
        end = sourceSize; 

        if (end < start) 
        { 
         end = start; 
        } 
       } 

       float sum = 0; 
       result[i] = new Weights(); 

       List<Weight> builder = new List<Weight>(); 
       for (int a = start; a < end; a++) 
       { 
        float w = sampler.GetValue((a - center)/scale); 

        if (w < 0 || w > 0) 
        { 
         sum += w; 
         builder.Add(new Weight(a, w)); 
        } 
       } 

       // Normalise the values 
       if (sum > 0 || sum < 0) 
       { 
        builder.ForEach(w => w.Value /= sum); 
       } 

       result[i].Values = builder.ToArray(); 
       result[i].Sum = sum; 
      }); 

    return result; 
} 

/// <summary> 
/// Represents the weight to be added to a scaled pixel. 
/// </summary> 
protected class Weight 
{ 
    /// <summary> 
    /// The pixel index. 
    /// </summary> 
    public readonly int Index; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="Weight"/> class. 
    /// </summary> 
    /// <param name="index">The index.</param> 
    /// <param name="value">The value.</param> 
    public Weight(int index, float value) 
    { 
     this.Index = index; 
     this.Value = value; 
    } 

    /// <summary> 
    /// Gets or sets the result of the interpolation algorithm. 
    /// </summary> 
    public float Value { get; set; } 
} 

/// <summary> 
/// Represents a collection of weights and their sum. 
/// </summary> 
protected class Weights 
{ 
    /// <summary> 
    /// Gets or sets the values. 
    /// </summary> 
    public Weight[] Values { get; set; } 

    /// <summary> 
    /// Gets or sets the sum. 
    /// </summary> 
    public float Sum { get; set; } 
} 

प्रत्येक आईरसप्लर दिए गए सूचकांक के आधार पर वजन की उचित श्रृंखला प्रदान करता है। निम्नानुसार bicubic resampler काम करता है।

/// <summary> 
/// The function implements the bicubic kernel algorithm W(x) as described on 
/// <see href="https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm">Wikipedia</see> 
/// A commonly used algorithm within imageprocessing that preserves sharpness better than triangle interpolation. 
/// </summary> 
public class BicubicResampler : IResampler 
{ 
    /// <inheritdoc/> 
    public float Radius => 2; 

    /// <inheritdoc/> 
    public float GetValue(float x) 
    { 
     // The coefficient. 
     float a = -0.5f; 

     if (x < 0) 
     { 
      x = -x; 
     } 

     float result = 0; 

     if (x <= 1) 
     { 
      result = (((1.5f * x) - 2.5f) * x * x) + 1; 
     } 
     else if (x < 2) 
     { 
      result = (((((a * x) + 2.5f) * x) - 4) * x) + 2; 
     } 

     return result; 
    } 
} 

मौजूदा एल्गोरिदम द्वारा आकार की गई छवि का एक उदाहरण यहां दिया गया है। आउटपुट सही है (नोट करें कि चांदी की शीन संरक्षित है)।

मूल छवि

Original unscaled image

छवि bicubic रीसैंपलर का उपयोग कर आकार में आधा कर दिया।

Image halved in size

कोड एक बहुत बड़ा library कि मैं corefx लिए इमेज प्रोसेसिंग जोड़ने के लिए लिख रहा हूँ का हिस्सा है।

+0

इंडेक्स और वजन मूल्य क्या हैं? इसके बिना रिकर्सन उपखंड की गणना करने का कोई तरीका नहीं है ... अन्य तो क्रूर बल ... और केवल तभी कुछ गुणों को पूरा किया जाता है ... फ़िल्टरिंग से पहले और बाद में नमूना छवि साझा करना भी एक अच्छा विचार होगा, इसलिए कुछ ऐसा है तुलना करें ... – Spektre

+0

@Spektre मैंने प्रश्न के लिए बहुत अधिक जानकारी जोड़ दी है। यदि आपको कुछ और चाहिए तो कृपया मुझे बताएं। –

+0

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

उत्तर

0

ठीक है तो यहां मैं इसके बारे में कैसे गया।

चाल पहली बार मूल छवि के समान ऊंचाई को रखने वाली छवि की चौड़ाई का आकार बदलने के लिए है। हम परिणामस्वरूप पिक्सल को अस्थायी छवि में संग्रहीत करते हैं।

फिर यह उस छवि को हमारे अंतिम आउटपुट में आकार देने का एक मामला है।

जैसा कि आप देख सकते हैं कि हम अब प्रत्येक पिक्सेल पर वजन संग्रह दोनों के माध्यम से पुनरावृत्त नहीं कर रहे हैं। पुनरावृत्त होने के बावजूद बाहरी पिक्सेल लूप दो बार एल्गोरिदम अपने परीक्षण छवियों में लगभग 25% तेज औसत ऑपरेशन में बहुत तेज था।

// Interpolate the image using the calculated weights. 
// First process the columns. 
Parallel.For(
    0, 
    sourceBottom, 
    y => 
    { 
     for (int x = startX; x < endX; x++) 
     { 
      Weight[] horizontalValues = this.HorizontalWeights[x].Values; 

      // Destination color components 
      Color destination = new Color(); 

      foreach (Weight xw in horizontalValues) 
      { 
       int originX = xw.Index; 
       Color sourceColor = Color.Expand(source[originX, y]); 
       destination += sourceColor * xw.Value; 
      } 

      destination = Color.Compress(destination); 
      this.firstPass[x, y] = destination; 
     } 
    }); 

// Now process the rows. 
Parallel.For(
    startY, 
    endY, 
    y => 
    { 
     if (y >= targetY && y < targetBottom) 
     { 
      Weight[] verticalValues = this.VerticalWeights[y].Values; 

      for (int x = startX; x < endX; x++) 
      { 
       // Destination color components 
       Color destination = new Color(); 

       foreach (Weight yw in verticalValues) 
       { 
        int originY = yw.Index; 
        int originX = x; 
        Color sourceColor = Color.Expand(this.firstPass[originX, originY]); 
        destination += sourceColor * yw.Value; 
       } 

       destination = Color.Compress(destination); 
       target[x, y] = destination; 
      } 
     } 
    }); 
+0

बड़ी गति लाभ तब होते हैं जब आप विशेष रूप से स्मृति के लगातार क्षेत्रों को पढ़ रहे होते हैं। ऐसा करने के लिए, आपको मैट्रिक्स को दो बार स्थानांतरित करना होगा - एक्स स्केल करने के बाद, फिर वाई स्केल करने के बाद। उल्टा यह है कि आप लिखने के संचालन के दौरान स्थानांतरित कर सकते हैं, जो सीपीयू असीमित रूप से कर सकता है अगर स्मृति के उस क्षेत्र पर कोई पढ़ने निर्भरता नहीं है । –

+0

यह आपके कोड को सरल बनाना चाहिए - अब आप केवल एक दिशा में स्केल करने के बारे में जानते हैं, और लिखते समय आप हमेशा ट्रांसफर करते हैं। आप स्केल 1 डी को दो बार कॉल करते हैं, प्रत्येक बार विभिन्न चौड़ाई/ऊंचाई और भारोत्तोलन संरचनाओं में गुज़रते हैं। अब मैंने केवल सी में इसका परीक्षण किया है, इसलिए सरणी सीमाओं की जांच चीजों को धीमा कर सकती है, लेकिन मुझे लगता है कि अंतर्निहित कैश कोहिरेंसी लाभ सीएलआर जेआईटी के भीतर भी काम करना चाहिए। यह ट्रांसपोज़-कब-लेखन, अकेले पढ़ने-लगातार दृष्टिकोण प्रदर्शन लाभ में परिमाण के क्रम के करीब प्रदान करना प्रतीत होता था। –

1

आप भारित वोरोनोई आरेख का प्रयास कर सकते हैं। यादृच्छिक रूप से अंक का एक सेट आज़माएं और voronoi आरेख की गणना करें। लॉयड के एल्गोरिदम के साथ बहुभुज को चिकना करें और बहुभुज के रंग को अलग करें। भार भारित voronoi आरेख गणना करें। उदाहरण के लिए voronoi stippling और मोज़ेक: http://www.mrl.nyu.edu/~ajsecord/stipples.html और http://www.evilmadscientist.com/2012/stipplegen-weighted-voronoi-stippling-and-tsp-paths-in-processing/

+0

धन्यवाद, मैं इसे देख लूंगा। –

1

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

+0

मुझे लगता है कि मैं जो कह रहा हूं उसका पालन करता हूं धन्यवाद। मैं उस दृष्टिकोण के साथ प्रयोग करूंगा। –

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