ทั้งคู่เป็นภาพระดับสีเทา


23

ปรับภาพสีเทาให้เป็นขาวดำบริสุทธิ์ด้วยอัลกอริทึมของคุณเอง

แนวทาง: คุณต้องคิดอัลกอริธึมใหม่ของคุณเอง คุณไม่สามารถใช้อัลกอริทึมที่มีอยู่แล้ว (เช่น Floyd-Steinburg) แต่คุณสามารถใช้เทคนิคทั่วไป โปรแกรมของคุณจะต้องสามารถอ่านภาพและสร้างภาพที่มีขนาดเท่ากัน นี่คือการประกวดความนิยมดังนั้นใครก็ตามที่ชนะได้ดีที่สุด (ใกล้เคียงกับต้นฉบับมากที่สุด) และสร้างสรรค์มากที่สุด (พิจารณาจากคะแนนโหวต) โบนัสหากรหัสย่อแม้ว่าจะไม่จำเป็นก็ตาม

คุณสามารถใช้ภาพสีเทาใดก็ได้ที่คุณต้องการเป็นอินพุตควรมีขนาดใหญ่กว่า 300x300 รูปแบบไฟล์ใดก็ได้

อินพุตตัวอย่าง:

ลูกสุนัข

ตัวอย่างผลลัพธ์:

dithered

นี่เป็นงานที่ค่อนข้างดี แต่ก็ยังมีเส้นและลวดลายที่มองเห็นได้


4
+1 สำหรับความท้าทายที่น่าสนใจ แต่ฉันคิดว่านี่น่าจะดีกว่า [code-golf] (พร้อม spec) หรือเกณฑ์อื่น ๆ ที่สมบูรณ์
ลูกบิดประตู

2
ปัญหาเกี่ยวกับการใช้รหัสขนาดความเร็วและหน่วยความจำคือคุณต้องมีเกณฑ์วัตถุประสงค์เพื่อให้ทราบว่าผลลัพธ์ที่ได้นั้นจะต้องเป็นคำตอบที่ถูกต้องซึ่งเป็นไปไม่ได้เลยทีเดียว การประกวดความนิยมนั้นสมเหตุสมผล แต่ไม่มีข้อ จำกัด ใด ๆ เกี่ยวกับรหัสไม่มีแรงจูงใจให้คนคิดนอกกรอบ ฉันอยากจะตอบคำถามที่ฉลาดกว่าคำตอบที่ฉลาดกว่าเพราะมันเพิ่งใช้อัลกอริทึมที่มีอยู่แล้ว แต่คุณกำลังสร้างแรงจูงใจให้กับหลัง
Martin Ender

3
เส้นแบ่งระหว่างอัลกอริธึมและเทคนิคบางเกินไปที่จะตัดสินว่ามีบางสิ่งบางอย่างตกหล่น
Peter Taylor

2
ฉันคิดว่าการเปรียบเทียบผลลัพธ์จะง่ายกว่ามากหากพวกเขาทั้งหมดแสดงผลลัพธ์จากภาพเดียวกัน
joeytwiddle

3
คุณสามารถเพิ่มแหล่งที่มาของภาพได้หรือไม่? (ผมไม่คิดว่าคนจะโกรธเพื่อดู / ภาพของเขาของเธอที่นี่ แต่มันยุติธรรมที่จะอ้างอิงแหล่งที่มา)
อัล

คำตอบ:


16

Fortran

โอเคฉันใช้รูปแบบภาพคลุมเครือที่เรียกว่า FITS ซึ่งใช้สำหรับดาราศาสตร์ ซึ่งหมายความว่ามีห้องสมุด Fortran สำหรับการอ่านและการเขียนภาพดังกล่าว นอกจากนี้ ImageMagick และ Gimp ยังสามารถอ่าน / เขียนภาพที่เหมาะกับ

อัลกอริทึมที่ฉันใช้นั้นขึ้นอยู่กับการทำ dithering "Sierra Lite" แต่มีการปรับปรุงสองอย่าง:
a) ฉันลดข้อผิดพลาดที่แพร่กระจายโดยปัจจัย 4/5
b) ฉันแนะนำการเปลี่ยนแปลงแบบสุ่มในเมทริกซ์การแพร่ในขณะที่รักษาค่าคงที่ผลรวม
ร่วมกันกำจัดรูปแบบเหล่านี้เกือบทั้งหมดที่เห็นในตัวอย่าง OPs

สมมติว่าคุณติดตั้งไลบรารี 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 เอาต์พุตตัวอย่าง:
OPs dithered ภาพของลูกสุนัข


สิ่งนี้ดูดีมากอาจไม่สามารถเอาชนะคุณภาพได้
aditsu

ขอบคุณ! ฉันไม่รู้ว่ามันเอาชนะไม่ได้ แต่มันจะยาก (ส่วนตัวมาก) ที่จะตัดสินสิ่งนี้จากอัลกอริทึมที่ดีอื่น ๆ
รอบนอก

1
ฉันรู้ว่าฉันตีกอล์ฟด้วยการใช้ความเข้ากันได้แบบย้อนหลัง แต่จริงๆแล้วมันดูเหมือนว่าคุณใช้มันในทางที่ผิด รหัสนี้ทำให้ฉันร้องไห้จริงๆ
Kyle Kanos

@ KyleKanos ฉันมีความสุขเสมอเมื่อรหัสของฉันทำให้ใครบางคนร้องไห้: p ในหัวข้อแม้ว่าสิ่งที่น่ากลัวที่นี่โดยเฉพาะคืออะไร? ใช่ฉันสามารถใช้คำว่า "ไม่บอกเป็นนัย" แต่ความสนุกในนั้นอยู่ที่ไหน ฉันใช้เพื่อการเขียนโค้ดอย่างจริงจังในที่ทำงาน แต่ไม่ใช่สำหรับการเล่นกอล์ฟ และฉันตกลงอย่างแน่นอนว่า CFITSIO library API นั้นน่ากลัวอย่างสมบูรณ์ (ftppre () แสดงผลภาพในความแม่นยำที่แท้จริงเพียงครั้งเดียว ftpprj () แสดงผลภาพด้วยความแม่นยำจำนวนเต็มสองเท่าเป็นต้น) แต่นั่นเป็น F77 ที่เข้ากันได้ย้อนหลังสำหรับคุณ
รอบนอก

1
ตกลงดังนั้นส่วนใหญ่เป็นเพียงฉันเป็นเลอะเทอะ ฉันปรับปรุงมัน คำติชมที่สร้างสรรค์ได้รับการชื่นชมเสมอ :)
กึ่ง extrinsic

34

GraphicsMagick / ImageMagick

สั่ง Dither:

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

ก่อนที่จะบ่นเกี่ยวกับการใช้ "อัลกอริทึมที่กำหนดไว้" โปรดอ่าน ChangeLog สำหรับ GraphicsMagick และ ImageMagick สำหรับเดือนเมษายน 2003 ซึ่งคุณจะเห็นว่าฉันใช้อัลกอริทึมในแอปพลิเคชันเหล่านั้น นอกจากนี้การรวมกันของ "-gamma .45455" กับ "-ordered-dither" เป็นเรื่องใหม่

"-gamma .45455" ดูแลภาพที่เบาเกินไป พารามิเตอร์ "all" จำเป็นสำหรับ GraphicsMagick เท่านั้น

มีแถบเนื่องจากมีเพียง 17 ระดับสีเทาในภาพ 4x4 dither สั่ง การปรากฏตัวของแถบคาดสามารถลดลงได้โดยใช้ 8x8 ซึ่งมีสองระดับ 65 ระดับ

นี่คือภาพต้นฉบับ 4x4 และ 8x8 สั่งให้เอาท์พุท dithered และเอาท์พุทเกณฑ์แบบสุ่ม: ป้อนคำอธิบายรูปภาพที่นี่

ฉันชอบเวอร์ชันที่เรียงลำดับแล้ว แต่ฉันกำลังรวมรุ่นสุ่มเกณฑ์เพื่อความสมบูรณ์

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

"10x90%" หมายถึงการแสดงพิกเซลที่มีความเข้มต่ำกว่า 10 เปอร์เซ็นต์เป็นสีดำบริสุทธิ์และสูงกว่า 90 เปอร์เซ็นต์เป็นสีขาวบริสุทธิ์เพื่อหลีกเลี่ยงการมีจุดเล็ก ๆ โดดเดี่ยวในพื้นที่เหล่านั้น

อาจเป็นที่น่าสังเกตว่าทั้งคู่มีประสิทธิภาพด้านหน่วยความจำเท่าที่ควรจะเป็น ไม่มีการแพร่ใด ๆ ดังนั้นพวกเขาจึงทำงานทีละหนึ่งพิกเซลแม้ในขณะที่เขียนบล็อกที่เรียงตามลำดับและไม่จำเป็นต้องรู้อะไรเกี่ยวกับพิกเซลข้างเคียง ImageMagick และ GraphicsMagick ประมวลผลทีละแถว แต่ไม่จำเป็นสำหรับวิธีการเหล่านี้ การแปลงตามคำสั่งนั้นใช้เวลาน้อยกว่า. 04 วินาทีในคอมพิวเตอร์ x86_64 เครื่องเก่าของฉัน


31
"ก่อนที่จะบ่นเกี่ยวกับการใช้" อัลกอริทึมที่กำหนดไว้ "โปรดอ่าน ChangeLog สำหรับ GraphicsMagick และ ImageMagick สำหรับเดือนเมษายน 2003 ซึ่งคุณจะเห็นว่าฉันใช้อัลกอริทึมในแอปพลิเคชันเหล่านั้น" +1 สำหรับแก้มที่แท้จริง
Joe Z.

22

ฉันขอโทษสำหรับรูปแบบรหัสฉันโยนสิ่งนี้เข้าด้วยกันโดยใช้ห้องสมุดที่เราเพิ่งสร้างขึ้นในคลาส Java ของฉันและมันมีกรณีที่ไม่ดีของการคัดลอกและวางหมายเลขมายากล อัลกอริธึมเลือกสุ่มสี่เหลี่ยมในภาพและตรวจสอบว่าความสว่างเฉลี่ยมากกว่าในภาพ dithered หรือภาพต้นฉบับ จากนั้นจะเปิดหรือปิดพิกเซลเพื่อให้ความสว่างเข้ามาใกล้มากขึ้นโดยเฉพาะการเลือกพิกเซลที่แตกต่างจากภาพต้นฉบับมากขึ้น ฉันคิดว่ามันทำงานได้ดีกว่าที่จะนำรายละเอียดบาง ๆ ออกมาเหมือนกับผมของลูกสุนัข แต่ภาพนั้นดูน่าสนใจเพราะมันพยายามที่จะดึงรายละเอียดออกมาแม้ในพื้นที่ที่ไม่มีเลย

ป้อนคำอธิบายรูปภาพที่นี่

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 วินาทีบนคอมพิวเตอร์ของฉัน
QuadmasterXLII

2
แม้ว่าอาจจะไม่ใช่อัลกอริธึมความจงรักภักดีที่ยิ่งใหญ่ที่สุด แต่ผลลัพธ์ก็เป็นงานศิลปะด้วยตนเอง
AJMansfield

4
ฉันชอบรูปลักษณ์ของอัลกอริทึมนี้มาก! แต่ฉันคิดว่าบางทีมันก็ดูดีไปหน่อยเพราะมันให้เนื้อสัมผัสที่คล้ายกับขนและมันเป็นสัตว์ที่มีขน แต่ฉันไม่แน่ใจว่านี่เป็นเรื่องจริง คุณช่วยโพสต์รูปอีกภาพของรถได้ไหม
กึ่งกลางต่างประเทศ

1
ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุดทั้งในแง่ของความคิดริเริ่มของอัลกอริทึมและในแง่ของผลลัพธ์ที่ยอดเยี่ยม ฉันก็อยากจะเห็นมันทำงานกับภาพอื่น ๆ เช่นกัน
นาธาเนียล

13

Ghostscript (ด้วยความช่วยเหลือเล็กน้อยของ 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
นี่คือเฮฮา ฉันตกตะลึงกับความจริงที่ว่าไม่มีใครให้ความเห็นเกี่ยวกับสิ่งมหัศจรรย์สไตล์ Warhol นี้
Andreï Kostyrka

10

JAVA

นี่คือการส่งของฉัน ใช้ภาพ JPG คำนวณความสว่างของพิกเซลต่อพิกเซล (ขอบคุณ Bonan ในคำถาม SO นี้ ) จากนั้นตรวจสอบกับรูปแบบสุ่มเพื่อให้ทราบว่าพิกเซลที่ได้นั้นจะเป็นสีดำหรือสีขาว พิกเซล Darkerst จะเป็นสีดำเสมอและพิกเซลที่สว่างที่สุดจะเป็นสีขาวเสมอเพื่อรักษารายละเอียดของภาพ

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 จากนั้นใช้รูปแบบที่สอดคล้องกันของ 4 บิตเพื่อสร้างพิกเซลดำและขาว 2 * 2
นั่นก็หมายความว่าความกว้างและความสูงต้องเท่ากัน

ตัวอย่าง:

ลูกสุนัข

และอัลกอริทึมแบบสุ่มเพียง 27 ไบต์:

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

มันใช้รูปแบบไฟล์เดียวกัน

ตัวอย่าง:

สุ่มลูกสุนัข

และในที่สุดก็เป็นวิธีการผสม: สุ่ม dithering กับอคติต่อรูปแบบกระดานหมากรุก; 44 ไบต์:

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

ตัวอย่าง:

ลูกสุนัขผสม


2
อันแรกเทียบได้กับแอพพลิเคชั่น "Flipnote Studio" ของ Nintendo DSi
BobTheAwesome

6

Java (1.4+)

ฉันไม่แน่ใจว่าฉันจะประดิษฐ์วงล้อใหม่ที่นี่หรือไม่ แต่ฉันคิดว่ามันอาจจะไม่ซ้ำกัน ...

ด้วยลำดับแบบสุ่มที่ จำกัด

ด้วยลำดับแบบสุ่ม จำกัด

dithering บริสุทธิ์แบบสุ่ม

dithering บริสุทธิ์แบบสุ่ม

ป้อนคำอธิบายรูปภาพที่นี่

ภาพเมืองจากคำตอบของ Averroes

อัลกอริทึมใช้แนวคิดของพลังงานความส่องสว่างที่มีการแปลและทำให้เป็นมาตรฐานเพื่อรักษาคุณสมบัติ รุ่นเริ่มต้นนั้นใช้ตัวกระวนกระวายใจแบบสุ่มในการผลิตดู dithered ผ่านพื้นที่ของความส่องสว่างที่คล้ายกัน อย่างไรก็ตามมันไม่ใช่สิ่งที่ดึงดูดสายตา ในการแก้ปัญหานี้ชุดลำดับที่ จำกัด แบบสุ่มที่ จำกัด จะถูกจับคู่กับความสว่างของพิกเซลอินพุตแบบดิบและตัวอย่างจะถูกใช้อย่างซ้ำ ๆ และให้ผลผลิตซ้ำ ๆ

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
ดีมาก. มันให้ผลที่แตกต่างจากคำตอบอื่น ๆ
Geobits

@Geobits ใช่มันทำให้ฉันประหลาดใจว่ามันมีประสิทธิภาพแค่ไหน แต่ผมไม่แน่ใจว่าผมจะเรียกมันว่า dithering ขณะที่มันผลิตออกค่อนข้างแตกต่างทางสายตา
Moogie

นั่นดูไม่เหมือนใครเลยทีเดียว
qwr

5

หลาม

ความคิดที่จะต่อไปนี้: ภาพที่ได้รับการแบ่งออกเป็นn x nกระเบื้อง เราคำนวณสีเฉลี่ยของไพ่แต่ละใบ จากนั้นเราแมปช่วงสี0 - 255กับช่วง0 - n*nที่ให้ค่าใหม่กับvเรา จากนั้นเราจะระบายสีพิกเซลทั้งหมดจากไทล์สีดำนั้นและvพิกเซลสีแบบสุ่มภายในไทล์สีขาวนั้น มันอยู่ไกลจากที่ดีที่สุด แต่ก็ยังให้ผลลัพธ์ที่เราจำได้ ทั้งนี้ขึ้นอยู่กับความละเอียดก็ทำงานมักจะดีที่สุดหรือn=2 n=3ในขณะที่อยู่ในn=2คุณสามารถค้นหาสิ่งประดิษฐ์จาก 'ความลึกของสีที่จำลองไว้' ในกรณีที่n=3มันสามารถเบลอได้บ้าง ฉันคิดว่าภาพควรมีขนาดเท่ากัน แต่แน่นอนว่าคุณสามารถใช้วิธีนี้และเพิ่มขนาดของภาพที่สร้างขึ้นสองเท่า / สามเท่าเพื่อรับรายละเอียดเพิ่มเติม

PS: ฉันรู้ว่าฉันมาช้าไปงานปาร์ตี้ฉันจำได้ว่าฉันไม่มีความคิดใด ๆ เมื่อความท้าทายเริ่มต้นขึ้น แต่ตอนนี้เพิ่งมีคลื่นสมองนี้ =)

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 * hไบต์ของค่าระดับสีเทาตั้งแต่ 0 ถึง 255:

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

จากนั้นเราสามารถกำหนดชิ้นส่วนของรหัสใน Python (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')

ซึ่ง "dithers" โดยส่งคืนสีขาวหรือสีดำที่มีความน่าจะเป็นเท่ากับค่าระดับสีเทาของพิกเซลนั้น


ใช้กับภาพตัวอย่างมันให้อะไรแบบนี้

สุนัขขนนก

มันไม่ได้สวยเกินไป แต่มันจะดูคล้ายกันมากเมื่อลดขนาดลงในหน้าตัวอย่างและสำหรับ Python เพียง 145 ไบท์ฉันไม่คิดว่าคุณจะทำได้ดีกว่านี้อีก


คุณสามารถแบ่งปันตัวอย่างได้ไหม? ผมเชื่อว่านี่เป็น dithering สุ่มและผลที่ไม่ได้เป็น ... รูปประวัติสะอาดที่ดีแม้ว่า
qwr

นี่คือการสุ่มแบบสุ่มและฉันกำลังทำตัวอย่างภาพตัวอย่างของคุณในขณะนี้
Joe Z.

2
ฉันคิดว่ามันอาจได้ประโยชน์จากการเพิ่มความคมชัด ฉันไม่รู้หลาม แต่ฉันถือว่า random.randint (0,255) กำลังเลือกหมายเลขสุ่มระหว่าง 0 ถึง 255 ลอง จำกัด ให้อยู่ระหว่าง 55 ถึง 200 ซึ่งจะบังคับให้เฉดสีใดนอกสีดำหรือขาว ด้วยภาพจำนวนมากคุณจะได้ภาพที่ดีและโดดเด่นโดยไม่เกะกะเพียงแค่เกณฑ์ที่เรียบง่าย (การเพิ่มความคมชัดแบบสุ่ม + จะทำให้ภาพอยู่ตรงกลางระหว่างภาพปัจจุบันของคุณและเกณฑ์แบบง่าย)
ระดับแม่น้ำเซนต์

ฉันคิดว่าการทำ dithering แบบสุ่มควรเรียกว่า Geith dithering (เพราะดูเหมือนว่าเอาต์พุตของตัวนับ Geiger) ตกลงใคร
Joe Z.

1
นั่นเป็นสิ่งที่ ImageMagick และ GraphicsMagick ทำกับตัวเลือก "-random-threshold" ที่ฉันเพิ่มพร้อมกับ "-ordered-dither" เมื่อหลายปีก่อน (เพิ่มในคำตอบของฉัน) อีกครั้งการชนแกมม่าจะช่วยให้ได้รับความเข้มที่เหมาะสม ฉันเห็นด้วยกับคำแนะนำ "Geiger dithering"
Glenn Randers-Pehrson

3

งูเห่า

ใช้ไฟล์ PNG / BMP 24 บิตหรือ 32 บิต (JPG สร้างเอาต์พุตที่มีสีเทาบางส่วนอยู่) นอกจากนี้ยังสามารถขยายไปยังไฟล์ที่มีสี

มันใช้ ELA ที่ปรับความเร็วให้เหมาะสมเพื่อให้ภาพเป็นสี 3 บิตซึ่งจะกลับมาเป็นสีดำ / ขาวเมื่อให้ภาพทดสอบของคุณ

ฉันพูดถึงว่ามันเร็วจริงๆเหรอ?

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

มันดูดีและให้ตัวอย่างของการทำงานกับสีเช่นนี้
Οurous

2

ชวา

รหัสระดับต่ำโดยใช้PNGJและการเพิ่มจุดรบกวนพร้อมการกระจายแบบพื้นฐาน การใช้งานนี้ต้องการแหล่ง PNG ระดับสีเทา 8 บิต

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]);
    }

}

(เพิ่มโถนี้ไปยังเส้นทางการสร้างของคุณหากคุณต้องการลอง)

ป้อนคำอธิบายรูปภาพที่นี่

เป็นโบนัส: มันมีประสิทธิภาพอย่างมากในการใช้หน่วยความจำ (มันเก็บได้เพียงสามแถว) ดังนั้นจึงสามารถใช้สำหรับภาพขนาดใหญ่


Nitpick: ฉันคิดว่า "ใช้สำหรับภาพขนาดใหญ่" ไม่สำคัญ (คุณเคยเห็น PNG ระดับสีเทา> 8 GB หรือไม่) แต่ "การใช้กับอุปกรณ์แบบฝังตัว" เป็นจุดที่เด่นกว่ามาก
รอบ

ฉันชอบ แต่มันดูเบลอนิด ๆ
BobTheAwesome

1

ชวา

อัลกอริทึมที่ใช้ RNG ง่าย ๆ รวมถึงตรรกะบางอย่างสำหรับการจัดการกับภาพสี มีความน่าจะเป็น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();
    }
}

นี่คือผลลัพธ์ที่เป็นไปได้สำหรับภาพสุนัข:

ป้อนคำอธิบายรูปภาพที่นี่


ทำไมคุณไม่เพิ่มคำอธิบายลงในด้านบนแทนที่จะเป็นด้านล่างที่ไม่มีใครอ่านได้ ฉันชอบความคิดนั้น =)
ข้อบกพร่อง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.