या तो एक ग्रेस्केल छवि


23

या तो अपने स्वयं के एल्गोरिथ्म के साथ शुद्ध काले और सफेद रंग में एक ग्रेस्केल छवि।

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

आप इनपुट के रूप में जो भी स्केल इमेज चाहते हैं, उसका उपयोग कर सकते हैं, यह 300x300 से बड़ा होना चाहिए। कोई भी फ़ाइल प्रारूप ठीक है।

उदाहरण इनपुट:

कुत्ते का बच्चा

उदाहरण आउटपुट:

डिथर्ड

यह एक बहुत अच्छा काम है, लेकिन अभी भी दिखाई देने वाली लाइनें और पैटर्न हैं।


4
एक दिलचस्प चुनौती के लिए +1, लेकिन मुझे लगता है कि यह एक [कोड-गोल्फ] (एक कल्पना के साथ) या कुछ अन्य पूरी तरह से बेहतर दिशा के रूप में बेहतर होगा।
दरवाज़े

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

3
एक एल्गोरिथ्म और उसकी तकनीक के बीच की रेखा यह निर्धारित करने के लिए बहुत पतली है कि किस तरफ कुछ गिरता है।
पीटर टेलर

2
मुझे लगता है कि परिणामों की तुलना करना बहुत आसान होगा यदि वे सभी एक ही छवि से परिणाम दिखाते हैं।
joeytwiddle

3
क्या आप छवि का स्रोत जोड़ सकते हैं? (मुझे नहीं लगता कि यहां कोई व्यक्ति अपनी छवि देखकर नाराज होगा, लेकिन स्रोत का हवाला देना उचित है)
AL

जवाबों:


16

फोरट्रान

ठीक है, मैं एक अस्पष्ट छवि प्रारूप का उपयोग कर रहा हूं जिसे एफआईटीएस कहा जाता है जो खगोल विज्ञान के लिए उपयोग किया जाता है। इसका मतलब है कि ऐसी छवियों को पढ़ने और लिखने के लिए एक फोरट्रान पुस्तकालय है। इसके अलावा, ImageMagick और Gimp दोनों FITS इमेज को पढ़ / लिख सकते हैं।

मेरे द्वारा उपयोग किया जाने वाला एल्गोरिथ्म "सिएरा लाइट" पर आधारित है, लेकिन दो सुधारों के साथ:
ए) मैं एक कारक 4/5 द्वारा प्रचारित त्रुटि को कम करता हूं।
b) मैं अपने योग को स्थिर रखते हुए प्रसार मैट्रिक्स में एक यादृच्छिक भिन्नता का परिचय देता हूं।
साथ में ये ओपी उदाहरण में देखे गए पैटर्न को लगभग पूरी तरह से समाप्त कर देते हैं।

मान लें कि आपके पास CFITSIO लाइब्रेरी स्थापित है, जिसके साथ संकलित करें

gfortran -lcfitsio dither.f90

फ़ाइल नाम हार्ड-कोडेड हैं (इसे ठीक करने के लिए परेशान नहीं किया जा सकता)।

कोड:

program dither
  integer              :: status,unit,readwrite,blocksize,naxes(2),nfound
  integer              :: group,npixels,bitpix,naxis,i,j,fpixel,un
  real                 :: nullval,diff_mat(3,2),perr
  real, allocatable    :: image(:,:), error(:,:)
  integer, allocatable :: seed(:)
  logical              :: anynull,simple,extend
  character(len=80)    :: filename

  call random_seed(size=Nrand)
  allocate(seed(Nrand))
  open(newunit=un,file="/dev/urandom",access="stream",&
       form="unformatted",action="read",status="old")
  read(un) seed
  close(un)
  call random_seed(put=seed)
  deallocate(seed)

  status=0
  call ftgiou(unit,status)
  filename='PUPPY.FITS'
  readwrite=0
  call ftopen(unit,filename,readwrite,blocksize,status)
  call ftgknj(unit,'NAXIS',1,2,naxes,nfound,status)
  call ftgidt(unit,bitpix,status)
  npixels=naxes(1)*naxes(2)
  group=1
  nullval=-999
  allocate(image(naxes(1),naxes(2)))
  allocate(error(naxes(1)+1,naxes(2)+1))
  call ftgpve(unit,group,1,npixels,nullval,image,anynull,status)
  call ftclos(unit, status)
  call ftfiou(unit, status)

  diff_mat=0.0
  diff_mat(3,1) = 2.0 
  diff_mat(1,2) = 1.0
  diff_mat(2,2) = 1.0
  diff_mat=diff_mat/5.0

  error=0.0
  perr=0
  do j=1,naxes(2)
    do i=1,naxes(1)
      p=max(min(image(i,j)+error(i,j),255.0),0.0)
      if (p < 127.0) then
        perr=p
        image(i,j)=0.0
      else
        perr=p-255.0
        image(i,j)=255.0
      endif
      call random_number(r)
      r=0.6*(r-0.5)
      error(i+1,j)=  error(i+1,j)  +perr*(diff_mat(3,1)+r)
      error(i-1,j+1)=error(i-1,j+1)+perr*diff_mat(1,2)
      error(i  ,j+1)=error(i ,j+1) +perr*(diff_mat(2,2)-r)
    end do
  end do

  call ftgiou(unit,status)
  blocksize=1
  filename='PUPPY-OUT.FITS'
  call ftinit(unit,filename,blocksize,status)
  simple=.true.
  naxis=2
  extend=.true.
  call ftphpr(unit,simple,bitpix,naxis,naxes,0,1,extend,status)
  group=1
  fpixel=1
  call ftppre(unit,group,fpixel,npixels,image,status)
  call ftclos(unit, status)
  call ftfiou(unit, status)

  deallocate(image)
  deallocate(error)
end program dither

OPs पोस्ट में पिल्ला छवि के लिए उदाहरण आउटपुट:
पिल्ला की पथरीली तस्वीर
OPs उदाहरण आउटपुट:
ओपी ने पिल्ला की तस्वीर खींची


यह वास्तव में अच्छा लग रहा है, गुणवत्ता के लिए अपराजेय हो सकता है
ad16 4'14

धन्यवाद! मुझे नहीं पता कि यह अपराजेय है, लेकिन यह अन्य अच्छे एल्गोरिदम के खिलाफ न्याय करने के लिए कठिन (बहुत व्यक्तिपरक) होने वाला है।
अर्ध-बहिर्मंडल

1
मुझे पता है कि मैं पीछे की संगतता का दुरुपयोग करके गोल्फ कोड करता हूं, लेकिन यह वास्तव में ऐसा लगता है कि आप इसे मानक के रूप में दुरुपयोग करते हैं। यह कोड वास्तव में मुझे रुला रहा है।
काइल कानोस

@KyleKanos मुझे हमेशा खुशी होती है जब मेरा कोड किसी को रोता है: पी विषय पर हालांकि, यहां विशेष रूप से भयानक क्या है? हां, मैं "निहित कोई नहीं" का उपयोग कर सकता हूं, लेकिन इसमें मज़ा कहाँ है? मैं इसे काम में गंभीर कोडिंग के लिए उपयोग करता हूं, लेकिन गोल्फिंग के लिए नहीं। और मैं निश्चित रूप से सहमत हूँ कि CFITSIO पुस्तकालय एपीआई पूरी तरह से भयानक है (ftppre () एक वास्तविक परिशुद्धता में एक FITS छवि को आउटपुट करता है, ftpprj () डबल पूर्णांक सटीक में एक छवि को आउटपुट करता है, आदि) लेकिन यह आपके लिए F77 पीछे की संगतता है।
अर्ध-बहिर्मुखी

1
ठीक है, इसलिए उनमें से ज्यादातर सिर्फ मुझे मैला कर रहे थे। मैंने इसमें सुधार किया। रचनात्मक आलोचना की हमेशा सराहना की जाती है :)
अर्ध-बहिर्मुखी

34

GraphicsMagick / ImageMagick

आदेश दिया गया:

magick B2DBy.jpg -gamma .45455 -ordered-dither [all] 4x4 ordered4x4g45.pbm

"स्थापित एल्गोरिथ्म" का उपयोग करने के बारे में मेरी शिकायत करने से पहले, अप्रैल 2003 के लिए ChangeMog को GraphicsMagick और ImageMagick के लिए पढ़ें जहां आप देखेंगे कि मैंने उन अनुप्रयोगों में एल्गोरिथ्म लागू किया है। इसके अलावा, "-गर्मा -452" के साथ "-गम्मा .45455" का संयोजन नया है।

"-गामा" .45455 "छवि का बहुत हल्का होने का ख्याल रखता है। "सभी" पैरामीटर की आवश्यकता केवल GraphicsMagick के साथ है।

वहाँ बैंडिंग है क्योंकि 4x4 ऑर्डर किए गए छवि में केवल 17 ग्रेवलवेल हैं। बैंडिंग की उपस्थिति को 8x8 ऑर्डर किए गए का उपयोग करके कम किया जा सकता है, जिसमें 65 का स्तर है।

यहाँ मूल छवि, 4x4 और 8x8 डीथरेड आउटपुट और रैंडम-थ्रेशोल्ड आउटपुट का आदेश दिया गया है: यहाँ छवि विवरण दर्ज करें

मैं ऑर्डर किए गए-या तो संस्करण को पसंद करता हूं, लेकिन पूर्णता के लिए यादृच्छिक-दहलीज संस्करण शामिल करता हूं।

magick B2DBy.jpg -gamma .6 -random-threshold 10x90% random-threshold.pbm

"10x90%" का मतलब उन क्षेत्रों में कुछ एकाकी धब्बों से बचने के लिए, शुद्ध काले रंग के रूप में 10 प्रतिशत से नीचे और शुद्ध सफेद के रूप में 90 प्रतिशत से ऊपर प्रस्तुत करना है।

यह शायद ध्यान देने योग्य है कि दोनों स्मृति-कुशल हैं क्योंकि वे संभवतः हो सकते हैं। न तो कोई प्रसार होता है, इसलिए वे एक पिक्सेल का काम एक बार में करते हैं, यहां तक ​​कि ऑर्डर किए गए डेयर ब्लॉक को लिखते समय, और पड़ोसी पिक्सेल के बारे में कुछ भी जानने की आवश्यकता नहीं होती है। ImageMagick और GraphicsMagick एक समय में एक पंक्ति की प्रक्रिया करते हैं, लेकिन इन तरीकों के लिए यह आवश्यक नहीं है। मेरे पुराने x86_64 कंप्यूटर पर आदेश-दिनांकित रूपांतरण कम से कम .04 सेकंड वास्तविक समय लेते हैं।


31
"मेरे द्वारा" स्थापित एल्गोरिथ्म "का उपयोग करने के बारे में शिकायत करने से पहले, अप्रैल 2003 के लिए ChangeMog को GraphicsMagick और ImageMagick के लिए पढ़ें जहां आप देखेंगे कि मैंने उन अनुप्रयोगों में एल्गोरिथ्म लागू किया है।" सरासर गाल के लिए +1।
जो जेड

22

मैं कोड शैली के लिए माफी माँगता हूँ, मैंने इसे अपने जावा क्लास में निर्मित कुछ पुस्तकालयों का उपयोग करके फेंक दिया, और इसमें कॉपी-पेस्ट और मैजिक नंबरों का एक बुरा मामला है। एल्गोरिथ्म छवि में यादृच्छिक आयतों को चुनता है, और यह जांचता है कि क्या औसत चमक को मंद छवि या मूल छवि में अधिक है। यह तब रेखाओं के करीब चमक लाने के लिए पिक्सेल को चालू या बंद कर देता है, अधिमानतः ऐसे पिक्सेल उठाता है जो मूल छवि से अधिक भिन्न होते हैं। मुझे लगता है कि यह पिल्ला के बालों की तरह पतले विवरणों को सामने लाने के लिए एक बेहतर काम करता है, लेकिन छवि शोरहीन है क्योंकि यह किसी के साथ क्षेत्रों में भी विवरण बाहर लाने की कोशिश करता है।

यहाँ छवि विवरण दर्ज करें

public void dither(){
    int count = 0;
    ditheredFrame.suspendNotifications();
    while(count < 1000000){
        count ++;
        int widthw = 5+r.nextInt(5);
        int heightw = widthw;
        int minx = r.nextInt(width-widthw);
        int miny = r.nextInt(height-heightw);



            Frame targetCropped = targetFrame.crop(minx, miny, widthw, heightw);
            Frame ditherCropped = ditheredFrame.crop(minx, miny, widthw, heightw);

            if(targetCropped.getAverage().getBrightness() > ditherCropped.getAverage().getBrightness() ){
                int x = 0;
                int y = 0;
                double diff = 0;

                for(int i = 1; i < ditherCropped.getWidth()-1; i ++){
                    for(int j = 1; j < ditherCropped.getHeight()-1; j ++){
                        double d = targetCropped.getPixel(i,  j).getBrightness() - ditherCropped.getPixel(i, j).getBrightness();
                        d += .005* targetCropped.getPixel(i+1,  j).getBrightness() - .005*ditherCropped.getPixel(i+1, j).getBrightness();

                        d += .005* targetCropped.getPixel(i-1,  j).getBrightness() - .005*ditherCropped.getPixel(i-1, j).getBrightness();

                        d += .005* targetCropped.getPixel(i,  j+1).getBrightness() -.005* ditherCropped.getPixel(i, j+1).getBrightness();

                        d += .005* targetCropped.getPixel(i,  j-1).getBrightness() - .005*ditherCropped.getPixel(i, j-1).getBrightness();

                        if(d > diff){
                            diff = d;
                            x = i;
                            y = j;
                        }
                    }
                    ditherCropped.setPixel(x,  y,  WHITE);
                }

            } else {
                int x = 0;
                int y = 0;
                double diff = 0;

                for(int i = 1; i < ditherCropped.getWidth()-1; i ++){
                    for(int j = 1; j < ditherCropped.getHeight()-1; j ++){
                        double d =  ditherCropped.getPixel(i, j).getBrightness() -targetCropped.getPixel(i,  j).getBrightness();
                        d += -.005* targetCropped.getPixel(i+1,  j).getBrightness() +.005* ditherCropped.getPixel(i+1, j).getBrightness();

                        d += -.005* targetCropped.getPixel(i-1,  j).getBrightness() +.005* ditherCropped.getPixel(i+1, j).getBrightness();

                        d += -.005* targetCropped.getPixel(i,  j+1).getBrightness() + .005*ditherCropped.getPixel(i, j+1).getBrightness();

                        d += -.005* targetCropped.getPixel(i,  j-1).getBrightness() + .005*ditherCropped.getPixel(i, j-1).getBrightness();



                        if(d > diff){
                            diff = d;
                            x = i;
                            y = j;
                        }
                    }
                    ditherCropped.setPixel(x,  y,  BLACK);
                }
            }


    }
    ditheredFrame.resumeNotifications();
}

मैं इसे लेता हूं कि यह निर्धारक है? यदि हां, तो यह कितना तेज है?
Οurous

यह यादृच्छिक है, और मेरे कंप्यूटर पर लगभग 3 सेकंड लगते हैं।
क्वाडमास्टरएक्सएलआईआई

2
जबकि शायद सबसे बड़ी-निष्ठा एल्गोरिथ्म नहीं है, परिणाम सभी खुद से कला हैं।
AJMansfield

4
मैं वास्तव में इस एल्गोरिथ्म के रूप को पसंद करता हूं! लेकिन मुझे लगता है कि शायद यह आंशिक रूप से बहुत अच्छा लग रहा है क्योंकि यह फर के लिए लगभग समान रूप से बनावट का उत्पादन करता है, और यह फर के साथ एक जानवर है। लेकिन मुझे पूरा यकीन नहीं है कि यह सच है। क्या आप कार की दूसरी छवि पोस्ट कर सकते हैं?
अर्ध-बहिर्मुखी

1
मुझे लगता है कि यह एल्गोरिथम की मौलिकता और परिणामों की अजीबता के संदर्भ में, दोनों का सबसे अच्छा जवाब है। मैं वास्तव में इसे कुछ अन्य छवियों पर भी देखना चाहता हूं।
नथानिएल

13

घोस्टस्क्रिप्ट (ImageMagick की थोड़ी मदद से)

मेरा 'नया एल्गोरिथ्म' होने की बात तो दूर, लेकिन, अफसोस, इसका विरोध नहीं किया जा सका।

convert puppy.jpg puppy.pdf && \
convert puppy.jpg -depth 8 -colorspace Gray -resize 20x20! -negate gray:- | \
gs -q -sDEVICE=ps2write -o- -c \
    '<</Thresholds (%stdin) (r) file 400 string readstring pop 
       /HalftoneType 3 /Width 20 /Height 20
     >>sethalftone' \
    -f puppy.pdf | \
gs -q -sDEVICE=pngmono -o puppy.png -

यहाँ छवि विवरण दर्ज करें

बेशक यह 'समान आकार' के संयम के बिना बेहतर काम करता है।


2
यह उल्लासपूर्ण है। मैं इस तथ्य से दंग हूं कि किसी ने भी इस वारहोल शैली के चमत्कार पर टिप्पणी नहीं की है।
आंद्रे

10

जावा

यहाँ मेरा सबमिशन है। एक JPG छवि लेता है, पिक्सेल प्रति पिक्सेल चमकदारता की गणना करता है ( इस SO प्रश्न में बोनान के लिए धन्यवाद ) और फिर यह जानने के लिए यादृच्छिक पैटर्न के खिलाफ जांच करें कि क्या परिणामी पिक्सेल काला या सफेद होगा। गहरे रंग के पिक्सेल हमेशा काले रंग के होंगे और सबसे चमकीले पिक्सेल हमेशा छवि विवरण को संरक्षित करने के लिए सफेद होंगे।

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

import javax.imageio.ImageIO;

public class DitherGrayscale {

    private BufferedImage original;
    private double[] threshold = { 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31,
            0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42,
            0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53,
            0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64,
            0.65, 0.66, 0.67, 0.68, 0.69 };


    public static void main(String[] args) {
        DitherGrayscale d = new DitherGrayscale();
        d.readOriginal();
        d.dither();

    }

    private void readOriginal() {
        File f = new File("original.jpg");
        try {
            original = ImageIO.read(f);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void dither() {
        BufferedImage imRes = new BufferedImage(original.getWidth(),
                original.getHeight(), BufferedImage.TYPE_INT_RGB);
        Random rn = new Random();
        for (int i = 0; i < original.getWidth(); i++) {
            for (int j = 0; j < original.getHeight(); j++) {

                int color = original.getRGB(i, j);

                int red = (color >>> 16) & 0xFF;
                int green = (color >>> 8) & 0xFF;
                int blue = (color >>> 0) & 0xFF;

                double lum = (red * 0.21f + green * 0.71f + blue * 0.07f) / 255;

                if (lum <= threshold[rn.nextInt(threshold.length)]) {
                    imRes.setRGB(i, j, 0x000000);
                } else {
                    imRes.setRGB(i, j, 0xFFFFFF);
                }

            }
        }
        try {
            ImageIO.write(imRes, "PNG", new File("result.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

संसाधित छवि

अन्य उदाहरण:

मूल प्रसंस्कृत

इसके अलावा पूर्ण रंग छवियों के साथ काम करता है:

रंग छवि परिणाम


9

CJam

lNl_~:H;:W;Nl;1N[{[{ri}W*]{2/{:+}%}:P~}H*]z{P}%:A;H{W{I2/A=J2/=210/[0X9EF]=I2%2*J2%+m>2%N}fI}fJ

95 बाइट्स :)
यह इनपुट और आउटपुट दोनों के लिए कोई टिप्पणी लाइन के साथ ASCII PGM (P2) प्रारूप का उपयोग करता है ।

विधि बहुत ही बुनियादी है: यह 2 * 2 पिक्सल के वर्गों को जोड़ता है, 0..4 रेंज में कनवर्ट करता है, फिर 2 * 2-ब्लैक-वाइट पिक्सल जेनरेट करने के लिए 4 बिट्स के संगत पैटर्न का उपयोग करता है।
इसका मतलब यह भी है कि चौड़ाई और ऊंचाई भी होनी चाहिए।

नमूना:

नियतात्मक पिल्ला

और केवल 27 बाइट्स में एक यादृच्छिक एल्गोरिदम:

lNl_~*:X;Nl;1N{ri256mr>N}X*

यह उसी फ़ाइल प्रारूप का उपयोग करता है।

नमूना:

यादृच्छिक पिल्ला

और अंत में एक मिश्रित दृष्टिकोण: एक चेकरबोर्ड पैटर्न की ओर एक पूर्वाग्रह के साथ यादृच्छिक dithering; 44 बाइट्स:

lNl_~:H;:W;Nl;1NH{W{ri128_mr\IJ+2%*+>N}fI}fJ

नमूना:

मिश्रित पिल्ला


2
पहले वाला निन्टेंडो डीएसआई के "फ्लिपनोट स्टूडियो" एप्लिकेशन के साथ तुलनीय है।
BobTheAwesome

6

जावा (1.4+)

मुझे यकीन नहीं है कि क्या मैं यहां पहिया का फिर से आविष्कार कर रहा हूं, लेकिन मुझे लगता है कि यह अद्वितीय हो सकता है ...

सीमित यादृच्छिक दृश्यों के साथ

सीमित यादृच्छिक दृश्यों के साथ

शुद्ध यादृच्छिक dithering

शुद्ध यादृच्छिक dithering

यहाँ छवि विवरण दर्ज करें

एवरोसेस के उत्तर से शहर की छवि

एल्गोरिथ्म स्थानीयकृत चमक ऊर्जा की अवधारणा का उपयोग करता है और सुविधाओं को बनाए रखने के लिए सामान्य करता है। आरंभिक संस्करण ने तब समान चमक वाले क्षेत्रों पर एक घिसा हुआ रूप दिखाने के लिए एक यादृच्छिक घबराहट का उपयोग किया। हालाँकि ऐसा नहीं था कि नेत्रहीन अपील करते हैं। इसका मुकाबला करने के लिए, सीमित यादृच्छिक अनुक्रमों के एक सीमित सेट को कच्चे इनपुट पिक्सेल चमकदारता के लिए मैप किया जाता है और नमूनों को पुनरावृत्ति और बार-बार उपजाने वाले पृष्ठभूमि की तलाश में उपयोग किया जाता है।

import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;

public class LocalisedEnergyDitherRepeatRandom {

    static private final float EIGHT_BIT_DIVISOR=1.0F/256;
    private static final int KERNEL_SIZE_DIV_2 =4;
    private static final double JITTER_MULTIPLIER=0.01;
    private static final double NO_VARIANCE_JITTER_MULTIPLIER=1.5;
    private static final int RANDOM_SEQUENCE_LENGTH=10;
    private static final int RANDOM_SEQUENCE_COUNT=20;
    private static final boolean USE_RANDOM_SEQUENCES=true;

    public static void main(String args[]) throws Exception
    {
        BufferedImage image = ImageIO.read(new File("data/dog.jpg"));
        final int width = image.getWidth();
        final int height = image.getHeight();
        float[][][] yuvImage =convertToYUVImage(image);
        float[][][] outputYUVImage = new float[width][height][3];
        double circularKernelLumaMean,sum,jitter,outputLuma,variance,inputLuma;
        int circularKernelPixelCount,y,x,kx,ky;
        double[][] randomSequences = new double[RANDOM_SEQUENCE_COUNT][RANDOM_SEQUENCE_LENGTH];
        int[] randomSequenceOffsets = new int[RANDOM_SEQUENCE_COUNT];

        // Generate random sequences
        for (y=0;y<RANDOM_SEQUENCE_COUNT;y++)
        {
            for (x=0;x<RANDOM_SEQUENCE_LENGTH;x++)
            {
                randomSequences[y][x]=Math.random();
            }
        }

        for (y=0;y<height;y++)
        {
            for (x=0;x<width;x++)
            {
                circularKernelLumaMean=0;
                sum=0;
                circularKernelPixelCount=0;

                /// calculate the mean of the localised surrounding pixels contained in 
                /// the area of a circle surrounding the pixel.
                for (ky=y-KERNEL_SIZE_DIV_2;ky<y+KERNEL_SIZE_DIV_2;ky++)
                {
                    if (ky>=0 && ky<height)
                    {
                        for (kx=x-KERNEL_SIZE_DIV_2;kx<x+KERNEL_SIZE_DIV_2;kx++)
                        {
                            if (kx>=0 && kx<width)
                            {
                                double distance= Math.sqrt((x-kx)*(x-kx)+(y-ky)*(y-ky));

                                if (distance<=KERNEL_SIZE_DIV_2)
                                {
                                    sum+=yuvImage[kx][ky][0];
                                    circularKernelPixelCount++;
                                }
                            }
                        }
                    }
                }

                circularKernelLumaMean=sum/circularKernelPixelCount;

                /// calculate the variance of the localised surrounding pixels contained in 
                /// the area of a circle surrounding the pixel.
                sum =0;

                for (ky=y-KERNEL_SIZE_DIV_2;ky<y+KERNEL_SIZE_DIV_2;ky++)
                {
                    if (ky>=0 && ky<height)
                    {
                        for (kx=x-KERNEL_SIZE_DIV_2;kx<x+KERNEL_SIZE_DIV_2;kx++)
                        {
                            if (kx>=0 && kx<width)
                            {
                                double distance= Math.sqrt((x-kx)*(x-kx)+(y-ky)*(y-ky));

                                if (distance<=KERNEL_SIZE_DIV_2)
                                {
                                    sum+=Math.abs(yuvImage[kx][ky][0]-circularKernelLumaMean);
                                }
                            }
                        }
                    }
                }

                variance = sum/(circularKernelPixelCount-1);

                if (variance==0)
                {
                    // apply some jitter to ensure there are no large blocks of single colour
                    inputLuma=yuvImage[x][y][0];
                    jitter = Math.random()*NO_VARIANCE_JITTER_MULTIPLIER;
                }
                else
                {
                    // normalise the pixel based on localised circular kernel
                    inputLuma = outputYUVImage[x][y][0]=(float) Math.min(1.0, Math.max(0,yuvImage[x][y][0]/(circularKernelLumaMean*2)));
                    jitter = Math.exp(variance)*JITTER_MULTIPLIER;
                }

                if (USE_RANDOM_SEQUENCES)
                {
                    int sequenceIndex =(int) (yuvImage[x][y][0]*RANDOM_SEQUENCE_COUNT);
                    int sequenceOffset = (randomSequenceOffsets[sequenceIndex]++)%RANDOM_SEQUENCE_LENGTH;
                    outputLuma=inputLuma+randomSequences[sequenceIndex][sequenceOffset]*jitter*2-jitter;
                }
                else
                {
                    outputLuma=inputLuma+Math.random()*jitter*2-jitter;
                }


                // convert 8bit luma to 2 bit luma
                outputYUVImage[x][y][0]=outputLuma<0.5?0:1.0f;
            }
        }

        renderYUV(image,outputYUVImage);
        ImageIO.write(image, "png", new File("data/dog.jpg.out.png"));
    }

      /**
       * Converts the given buffered image into a 3-dimensional array of YUV pixels
       * @param image the buffered image to convert
       * @return 3-dimensional array of YUV pixels
       */
      static private float[][][] convertToYUVImage(BufferedImage image)
      {
        final int width = image.getWidth();
        final int height = image.getHeight();
        float[][][] yuv = new float[width][height][3];
        for (int y=0;y<height;y++)
        {
          for (int x=0;x<width;x++)
          {
            int rgb = image.getRGB( x, y );
            yuv[x][y]=rgb2yuv(rgb);
          }
        }
        return yuv;
      }

      /**
       * Renders the given YUV image into the given buffered image.
       * @param image the buffered image to render to
       * @param pixels the YUV image to render.
       * @param dimension the
       */
      static private void renderYUV(BufferedImage image, float[][][] pixels)
      {
        final int height = image.getHeight();
        final int width = image.getWidth();
        int rgb;

        for (int y=0;y<height;y++)
        {
          for (int x=0;x<width;x++)
          {

            rgb = yuv2rgb( pixels[x][y] );
            image.setRGB( x, y,rgb );
          }
        }
      }

      /**
       * Converts a RGB pixel into a YUV pixel
       * @param rgb a pixel encoded as 24 bit RGB
       * @return array representing a pixel. Consisting of Y,U and V components
       */
      static float[] rgb2yuv(int rgb)
      {
        float red = EIGHT_BIT_DIVISOR*((rgb>>16)&0xFF);
        float green = EIGHT_BIT_DIVISOR*((rgb>>8)&0xFF);
        float blue = EIGHT_BIT_DIVISOR*(rgb&0xFF);

        float Y = 0.299F*red + 0.587F * green + 0.114F * blue;
        float U = (blue-Y)*0.565F;
        float V = (red-Y)*0.713F;

        return new float[]{Y,U,V};
      }

      /**
       * Converts a YUV pixel into a RGB pixel
       * @param yuv array representing a pixel. Consisting of Y,U and V components
       * @return a pixel encoded as 24 bit RGB
       */
      static int yuv2rgb(float[] yuv)
      {
        int red = (int) ((yuv[0]+1.403*yuv[2])*256);
        int green = (int) ((yuv[0]-0.344 *yuv[1]- 0.714*yuv[2])*256);
        int blue = (int) ((yuv[0]+1.77*yuv[1])*256);

        // clamp to 0-255 range
        red=red<0?0:red>255?255:red;
        green=green<0?0:green>255?255:green;
        blue=blue<0?0:blue>255?255:blue;

        return (red<<16) | (green<<8) | blue;
      }

}

3
बहुत अच्छा। यह निश्चित रूप से अब तक अन्य उत्तरों की तुलना में एक अलग प्रभाव देता है।
फोब्र्स

@ जीबिट्स हाँ, इसने मुझे हैरान कर दिया कि यह कितना प्रभावी है। हालांकि मुझे यकीन नहीं है कि मैं इसे एक विचित्र कहूंगा क्योंकि यह काफी अलग तरह से अलग आउटपुट पैदा करता है
Moogie

यह काफी अनोखा दिखता है।
क्यूर १

5

अजगर

यह विचार निम्नलिखित है: छवि n x nटाइलों में विभाजित हो जाती है। हम उन टाइलों में से प्रत्येक के औसत रंग की गणना करते हैं। फिर हम रंग रेंज 0 - 255को उस श्रेणी में मैप करते हैं 0 - n*nजो हमें एक नया मान देता है v। फिर हम उस टाइल के काले रंग से सभी पिक्सेल को रंग देते हैं, और vउस टाइल के सफेद रंग के भीतर यादृच्छिक रूप से रंग के पिक्सेल। यह इष्टतम से बहुत दूर है लेकिन फिर भी हमें पहचानने योग्य परिणाम देता है। संकल्प के आधार पर, यह आमतौर पर सबसे अच्छा काम करता है n=2या n=3। जबकि n=2आप पहले से ही 'नकली रंग की गहराई से कलाकृतियों को पा सकते हैं, अगर n=3यह पहले से ही कुछ धुंधला हो सकता है। मैंने मान लिया कि छवियों को समान आकार में रहना चाहिए, लेकिन आप निश्चित रूप से इस विधि का उपयोग कर सकते हैं और अधिक विवरण प्राप्त करने के लिए उत्पन्न छवि के आकार को दोगुना / तिगुना कर सकते हैं।

पुनश्च: मुझे पता है कि मैं पार्टी के लिए थोड़ा देर से हूं, मुझे याद है कि मेरे पास कोई विचार नहीं था जब चुनौती शुरू हुई थी लेकिन अब बस इस मस्तिष्क की लहर थी =)

from PIL import Image
import random
im = Image.open("dog.jpg") #Can be many different formats.
new = im.copy()
pix = im.load()
newpix = new.load()
width,height=im.size
print([width,height])
print(pix[1,1])

window = 3 # input parameter 'n'

area = window*window
for i in range(width//window):     #loop over pixels
    for j in range(height//window):#loop over pixels
        avg = 0
        area_pix = []
        for k in range(window):
            for l in range(window):
                area_pix.append((k,l))#make a list of coordinates within the tile
                try:
                    avg += pix[window*i+k,window*j+l][0] 
                    newpix[window*i+k,window*j+l] = (0,0,0) #set everything to black
                except IndexError:
                    avg += 255/2 #just an arbitrary mean value (when were outside of the image)
                                # this is just a dirty trick for coping with images that have
                                # sides that are not multiples of window
        avg = avg/area
        # val = v is the number of pixels within the tile that will be turned white
        val = round(avg/255 * (area+0.99) - 0.5)#0.99 due to rounding errors
        assert val<=area,'something went wrong with the val'
        print(val)
        random.shuffle(area_pix) #randomize pixel coordinates
        for m in range(val):
            rel_coords = area_pix.pop()#find random pixel within tile and turn it white
            newpix[window*i+rel_coords[0],window*j+rel_coords[1]] = (255,255,255)

new.save('dog_dithered'+str(window)+'.jpg')

परिणाम:

n=2:

यहाँ छवि विवरण दर्ज करें

n=3:

यहाँ छवि विवरण दर्ज करें


3

आप चाहते हैं कि कोई भी फ़ाइल प्रारूप ठीक हो।

आइए इस प्रश्न के लिए एक बहुत ही कॉम्पैक्ट, सैद्धांतिक फ़ाइल प्रारूप को परिभाषित करें क्योंकि मौजूदा फ़ाइल स्वरूपों में से किसी के लिए एक त्वरित उत्तर लिखने के लिए बहुत अधिक ओवरहेड है।

छवि फ़ाइल के पहले चार बाइट को पिक्सेल में छवि की चौड़ाई और ऊंचाई को क्रमशः परिभाषित करें:

00000001 00101100 00000001 00101100
width: 300 px     height: 300 px

w * h0 से 255 तक ग्रेस्केल मूल्यों के बाइट्स के बाद :

10000101 10100101 10111110 11000110 ... [89,996 more bytes]

फिर, हम पायथन (145 बाइट्स) में कोड का एक टुकड़ा परिभाषित कर सकते हैं जो इस छवि को ले जाएगा और यह करेगा:

import random
f=open("a","rb");g=open("b","wb");g.write(f.read(4))
for i in f.read():g.write('\xff' if ord(i)>random.randint(0,255) else '\x00')

उस पिक्सेल के ग्रेस्केल मान के बराबर प्रायिकता के साथ सफेद या काले रंग की वापसी करके जो "डिटर्स" करता है।


नमूना छवि पर लागू, यह कुछ इस तरह देता है:

कुत्ते का बच्चा

यह बहुत सुंदर नहीं है, लेकिन यह बहुत समान दिखता है जब एक पूर्वावलोकन में छोटा होता है, और केवल 145 बाइट्स पायथन के लिए, मुझे नहीं लगता कि आप बहुत बेहतर प्राप्त कर सकते हैं।


क्या आप एक उदाहरण साझा कर सकते हैं? मेरा मानना ​​है कि यह यादृच्छिक dithering है, और परिणाम सबसे साफ नहीं हैं ... अच्छा प्रोफ़ाइल चित्र हालांकि
qwr

यह वास्तव में बेतरतीब dithering है, और मैं इस समय आपके नमूना चित्र का एक उदाहरण बना रहा हूं।
जो जेड

2
मुझे लगता है कि यह एक विपरीत बढ़ावा से लाभ हो सकता है। मैं अजगर को नहीं जानता, लेकिन मुझे लगता है कि random.randint (0,255) 0 और 255 के बीच एक यादृच्छिक संख्या उठा रहा है। 55 और 200 के बीच के बीच को सीमित करने का प्रयास करें, जो उस सीमा के बाहर किसी भी शेड को शुद्ध काले और सफेद होने के लिए मजबूर करेगा। कई चित्रों के साथ आप एक अच्छी, हड़ताली छवि प्राप्त कर सकते हैं जिसमें कोई डिटेरिंग नहीं है, बस एक साधारण दहलीज है। (रैंडम + कंट्रास्ट बूस्ट आपकी वर्तमान छवि और सरल सीमा के बीच एक छवि को मध्यवर्ती देगा।)
लेवल रिवर सेंट

मुझे लगता है कि यादृच्छिक डिटेरिंग को गीगर डीथियरिंग कहा जाना चाहिए (क्योंकि यह गीगर काउंटर के आउटपुट जैसा दिखता है)। कौन सहमत?
जो जेड

1
यह लगभग ठीक वही है जो ImageMagick और GraphicsMagick "-random-दहलीज" विकल्प के साथ करते हैं जो मैंने सालों पहले "-order-dither" के साथ जोड़ा था (मेरे उत्तर में जोड़ा गया)। फिर से, गामा को उछालने से सही तीव्रता प्राप्त करने में मदद मिलती है। मैं "Geiger dithering" सुझाव से सहमत हूं।
ग्लेन रैंडर्स-पीरसन

3

कोबरा

एक 24-बिट या 32-बिट PNG / BMP फ़ाइल लेता है (JPG इसमें कुछ ग्रे के साथ आउटपुट उत्पन्न करता है)। यह रंग युक्त फ़ाइलों के लिए भी एक्स्टेंसिबल है।

यह 3-बिट रंग में छवि को बेहतर बनाने के लिए गति-अनुकूलित ELA का उपयोग करता है, जो आपकी परीक्षण छवि दिए जाने पर काले / सफेद के रूप में वापस आ जाएगा।

क्या मैंने उल्लेख किया है कि यह वास्तव में तेज़ है?

use System.Drawing
use System.Drawing.Imaging
use System.Runtime.InteropServices
use System.IO

class BW_Dither

    var path as String = Directory.getCurrentDirectory to !
    var rng = Random()

    def main
        file as String = Console.readLine to !
        image as Bitmap = Bitmap(.path+"\\"+file)
        image = .dither(image)
        image.save(.path+"\\test\\image.png")

    def dither(image as Bitmap) as Bitmap

        output as Bitmap = Bitmap(image)

        layers as Bitmap[] = @[ Bitmap(image), Bitmap(image), Bitmap(image),
                                Bitmap(image), Bitmap(image), Bitmap(image),
                                Bitmap(image)]

        layers[0].rotateFlip(RotateFlipType.RotateNoneFlipX)
        layers[1].rotateFlip(RotateFlipType.RotateNoneFlipY)
        layers[2].rotateFlip(RotateFlipType.Rotate90FlipX)
        layers[3].rotateFlip(RotateFlipType.Rotate90FlipY)
        layers[4].rotateFlip(RotateFlipType.Rotate90FlipNone)
        layers[5].rotateFlip(RotateFlipType.Rotate180FlipNone)
        layers[6].rotateFlip(RotateFlipType.Rotate270FlipNone)

        for i in layers.length, layers[i] = .dither_ela(layers[i])

        layers[0].rotateFlip(RotateFlipType.RotateNoneFlipX)
        layers[1].rotateFlip(RotateFlipType.RotateNoneFlipY)
        layers[2].rotateFlip(RotateFlipType.Rotate270FlipY)
        layers[3].rotateFlip(RotateFlipType.Rotate270FlipX)
        layers[4].rotateFlip(RotateFlipType.Rotate270FlipNone)
        layers[5].rotateFlip(RotateFlipType.Rotate180FlipNone)
        layers[6].rotateFlip(RotateFlipType.Rotate90FlipNone)

        vals = List<of List<of uint8[]>>()
        data as List<of uint8[]> = .getData(output)
        for l in layers, vals.add(.getData(l))
        for i in data.count, for c in 3
            x as int = 0
            for n in vals.count, if vals[n][i][c] == 255, x += 1
            if x > 3.5, data[i][c] = 255 to uint8
            if x < 3.5, data[i][c] = 0 to uint8

        .setData(output, data)
        return output

    def dither_ela(image as Bitmap) as Bitmap

        error as decimal[] = @[0d, 0d, 0d]
        rectangle as Rectangle = Rectangle(0, 0, image.width, image.height)
        image_data as BitmapData = image.lockBits(rectangle, ImageLockMode.ReadWrite, image.pixelFormat) to !
        pointer as IntPtr = image_data.scan0
        bytes as uint8[] = uint8[](image_data.stride * image.height)
        pfs as int = Image.getPixelFormatSize(image.pixelFormat) // 8
        Marshal.copy(pointer, bytes, 0, image_data.stride * image.height)

        for y as int in image.height, for x as int in image.width
            position as int = (y * image_data.stride) + (x * pfs)
            for i in 3
                error[i] -= bytes[position + i]
                if Math.abs(error[i] + 255 - bytes[position + i]) < Math.abs(error[i] - bytes[position + i])
                    bytes[position + i] = 255 to uint8
                    error[i] += 255
                else, bytes[position + i] = 0 to uint8

        Marshal.copy(bytes, 0, pointer, image_data.stride * image.height)
        image.unlockBits(image_data)
        return image

    def getData(image as Bitmap) as List<of uint8[]>

        rectangle as Rectangle = Rectangle(0, 0, image.width, image.height)
        image_data as BitmapData = image.lockBits(rectangle, ImageLockMode.ReadOnly, image.pixelFormat) to !
        pointer as IntPtr = image_data.scan0
        bytes as uint8[] = uint8[](image_data.stride * image.height)
        pixels as List<of uint8[]> = List<of uint8[]>()
        for i in image.width * image.height, pixels.add(nil)
        pfs as int = Image.getPixelFormatSize(image.pixelFormat) // 8
        Marshal.copy(pointer, bytes, 0, image_data.stride * image.height)

        count as int = 0
        for y as int in image.height, for x as int in image.width
            position as int = (y * image_data.stride) + (x * pfs)
            if pfs == 4, alpha as uint8 = bytes[position + 3]
            else, alpha as uint8 = 255
            pixels[count] = @[
                                bytes[position + 2], #red
                                bytes[position + 1], #green
                                bytes[position],     #blue
                                alpha]               #alpha
            count += 1

        image.unlockBits(image_data)
        return pixels

    def setData(image as Bitmap, pixels as List<of uint8[]>)
        if pixels.count <> image.width * image.height, throw Exception()
        rectangle as Rectangle = Rectangle(0, 0, image.width, image.height)
        image_data as BitmapData = image.lockBits(rectangle, ImageLockMode.ReadWrite, image.pixelFormat) to !
        pointer as IntPtr = image_data.scan0
        bytes as uint8[] = uint8[](image_data.stride * image.height)
        pfs as int = Image.getPixelFormatSize(image.pixelFormat) // 8
        Marshal.copy(pointer, bytes, 0, image_data.stride * image.height)

        count as int = 0
        for y as int in image.height, for x as int in image.width
            position as int = (y * image_data.stride) + (x * pfs)
            if pfs == 4, bytes[position + 3] = pixels[count][3] #alpha
            bytes[position + 2] = pixels[count][0]              #red
            bytes[position + 1] = pixels[count][1]              #green
            bytes[position] = pixels[count][2]                  #blue
            count += 1

        Marshal.copy(bytes, 0, pointer, image_data.stride * image.height)
        image.unlockBits(image_data)

कुत्ता

पेड़


पुनरावृत्ति को कम करने के लिए, क्या आपने एक अस्थायी चर बनाने colऔर image.setPixel(x,y,col)बहुत अंत तक छोड़ने पर विचार किया है ?
joeytwiddle

3
पेड़ों की छवि के साथ क्या है?
AJMansfield

यह अच्छा लग रहा है, और रंगों के साथ काम करने का एक उदाहरण प्रदान करता है।
Οवस

2

जावा

निम्न स्तर का कोड, PNGJ और एक शोर जोड़ के साथ-साथ बुनियादी प्रसार का उपयोग करता है। इस कार्यान्वयन के लिए ग्रेस्केल 8-बिट PNG स्रोत की आवश्यकता होती है।

import java.io.File;
import java.util.Random;

import ar.com.hjg.pngj.ImageInfo;
import ar.com.hjg.pngj.ImageLineInt;
import ar.com.hjg.pngj.PngReaderInt;
import ar.com.hjg.pngj.PngWriter;

public class Dither {

    private static void dither1(File file1, File file2) {
        PngReaderInt reader = new PngReaderInt(file1);
        ImageInfo info = reader.imgInfo;
        if( info.bitDepth != 8 && !info.greyscale ) throw new RuntimeException("only 8bits grayscale valid");
        PngWriter writer = new PngWriter(file2, reader.imgInfo);
        ImageLineInt line2 = new ImageLineInt(info);
        int[] y = line2.getScanline();
        int[] ye = new int[info.cols];
        int ex = 0;
        Random rand = new Random();
        while(reader.hasMoreRows()) {
            int[] x = reader.readRowInt().getScanline();
            for( int i = 0; i < info.cols; i++ ) {
                int t = x[i] + ex + ye[i];
                y[i] = t > rand.nextInt(256) ? 255 : 0;
                ex = (t - y[i]) / 2;
                ye[i] = ex / 2;
            }
            writer.writeRow(line2);
        }
        reader.end();
        writer.end();
    }

    public static void main(String[] args) {
        dither1(new File(args[0]), new File(args[1]));
        System.out.println("See output in " + args[1]);
    }

}

( यदि आप इसे आज़माना चाहते हैं, तो अपने निर्माण पथ में जार जोड़ें )।

यहाँ छवि विवरण दर्ज करें

एक बोनस के रूप में: यह मेमोरी के उपयोग में अत्यंत कुशल है (यह केवल तीन पंक्तियों को संग्रहीत करता है) इसलिए इसका उपयोग विशाल चित्रों के लिए किया जा सकता है।


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

मुझे यह पसंद है लेकिन यह किनारों, मेथिंक के आसपास थोड़ा धुंधला दिखता है।
BobTheAwesome

1

जावा

बस एक सरल आरएनजी-आधारित एल्गोरिथ्म, प्लस रंग छवियों से निपटने के लिए कुछ तर्क। किसी भी पिक्सेल को सफ़ेद करने के लिए प्रायिकता b है, इसे अन्यथा काले पर सेट करता है; जहां b वह पिक्सेल की मूल चमक है।

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.Scanner;

import javax.imageio.ImageIO;


public class Ditherizer {
    public static void main(String[]a) throws IOException{
        Scanner input=new Scanner(System.in);
        Random rand=new Random();
        BufferedImage img=ImageIO.read(new File(input.nextLine()));
        for(int x=0;x<img.getWidth();x++){
            for(int y=0;y<img.getHeight();y++){
                Color c=new Color(img.getRGB(x, y));
                int r=c.getRed(),g=c.getGreen(),b=c.getBlue();
                double bright=(r==g&&g==b)?r:Math.sqrt(0.299*r*r+0.587*g*g+0.114*b*b);
                img.setRGB(x,y,(rand.nextInt(256)>bright?Color.BLACK:Color.WHITE).getRGB());    
            }
        }
        ImageIO.write(img,"jpg",new File(input.nextLine()));
        input.close();
    }
}

यहाँ कुत्ते की छवि के लिए एक संभावित परिणाम है:

यहाँ छवि विवरण दर्ज करें


आप नीचे के बजाय शीर्ष पर स्पष्टीकरण क्यों नहीं जोड़ते हैं जहां कोई भी इसे पढ़ने नहीं जा रहा है? मैं वास्तव में उस विचार =) को पसंद करता हूं
दोष
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.