ข้ามไปยังเนื้อหา
รูปและสื่อ

หยุด WordPress ไม่ให้บีบอัดรูปของคุณที่ 82%

WordPress เข้ารหัสไฟล์ JPEG ทุกไฟล์ที่มันสร้างขึ้นใหม่ที่คุณภาพ 82 บนสเกล 0–100 ค่าเริ่มต้นนี้อยู่ใน WP_Image_Editor::get_default_quality() และมีผลกับ

เผยแพร่แล้ว

WordPress เข้ารหัสไฟล์ JPEG ทุกไฟล์ที่มันสร้างขึ้นใหม่ที่คุณภาพ 82 บนสเกล 0–100 ค่าเริ่มต้นนี้อยู่ใน WP_Image_Editor::get_default_quality() และมีผลกับทุกไฟล์ย่อยที่ WordPress ตัดออกมาจากไฟล์ที่คุณอัพโหลด ไม่ว่าจะเป็น thumbnail, medium, medium_large, large และทุกขนาดพิเศษที่ธีมของคุณลงทะเบียนไว้ สำหรับภาพถ่าย ค่า 82 ถือเป็นจุดสมดุลที่สมเหตุสมผลระหว่างขนาดไฟล์กับ artifact แต่สำหรับสกรีนช็อต ภาพจับหน้าจอ UI แผนภูมิ โลโก้ และอะไรก็ตามที่มีข้อความคมชัดหรือสีเรียบ ค่า 82 จะทำลายภาพจนเห็นได้ชัด คุณจะเห็น mosquito noise ไต่วนอยู่รอบตัวอักษรทุกตัว และเกิด banding พาดผ่านการไล่เฉดสีที่ควรจะเนียน

วิธีเปลี่ยนคือเพิ่ม filter หนึ่งตัวแล้วสร้าง thumbnail ใหม่ ส่วนการแก้ปัญหาสกรีนช็อตโดยเฉพาะ คำตอบที่ดีกว่ามักจะเป็นการหยุดส่ง JPEG ให้ WordPress ตั้งแต่แรก

เลข 82 มาจากไหนกันแน่

WordPress ลดค่าเริ่มต้นของ JPEG จาก 90 เหลือ 82 ตั้งแต่เวอร์ชัน 4.5 (ปี 2016) เพื่อเป็นการปรับให้หน้าเว็บเบาลง ค่านี้ถูกส่งคืนโดย get_default_quality() ใน wp-includes/class-wp-image-editor.php และจะถูกอ่านทุกครั้งที่ editor ที่กำลังทำงานอยู่ (WP_Image_Editor_Imagick ถ้าโฮสต์ของคุณมี Imagick หรือไม่เช่นนั้นก็ WP_Image_Editor_GD) เขียนไฟล์ที่ปรับขนาดแล้ว

รายละเอียดสำคัญ: ค่า 82 ถูกใช้กับไฟล์ย่อยแต่ละขนาด ไม่ใช่แค่ครั้งเดียว เมื่อคุณอัพโหลดรูปหนึ่งรูป WordPress จะรันมันผ่านตัวเข้ารหัสหลายครั้ง โดยทำการ quantize ใหม่ที่คุณภาพ 82 ในทุกรอบ ตัวไฟล์ต้นฉบับที่อัพโหลดจะไม่ถูกแตะต้อง ถ้า มันมีขนาดต่ำกว่าเกณฑ์ big-image (2560px บนด้านที่ยาวที่สุด) แต่ถ้ามันเกินเกณฑ์นั้น WordPress จะสร้างไฟล์ -scaled.jpg ที่เข้ารหัสใหม่ที่คุณภาพ 82 แล้วเสิร์ฟ ไฟล์นั้น เป็นขนาด “full” ดังนั้นอาการ “ไฟล์ความละเอียดเต็มของฉันดูโอเค แต่เวอร์ชันที่อยู่บนหน้าเว็บดูเลอะ” เกือบทุกครั้งเป็นเพราะ srcset เลือกไฟล์ย่อยคุณภาพ 82 หรือไฟล์ -scaled

วิธีเปลี่ยนค่าคุณภาพ

มี filter หนึ่งตัวที่ต้อง hook wp_editor_set_quality คือตัวมาตรฐานสมัยใหม่ มันทำงานทั้งกับ JPEG และ WebP และรับค่า MIME type เข้ามาด้วย คุณจึงสามารถแยกเงื่อนไขได้:

add_filter( 'wp_editor_set_quality', function ( $quality, $mime_type ) {
    if ( 'image/jpeg' === $mime_type ) {
        return 92; // up from 82
    }
    return $quality;
}, 10, 2 );

วางโค้ดนี้ไว้ใน mu-plugin เล็กๆ (wp-content/mu-plugins/) หรือใน plugin เฉพาะของเว็บไซต์ จะใส่ใน functions.php ก็ได้ แต่คุณจะเสียการตั้งค่านี้ทันทีที่เปลี่ยนธีม

คุณอาจจะเจอคนแนะนำ filter ตัวเก่าคือ jpeg_quality ด้วย มันยังทำงานอยู่ (WordPress ใช้มันทันทีหลังจาก wp_editor_set_quality เฉพาะกับ JPEG) แต่ไม่มีเหตุผลที่จะ hook ทั้งสองตัว เพราะ wp_editor_set_quality ครอบคลุมทุกอย่างที่ตัวเก่าทำได้และมากกว่านั้น

สิ่งหนึ่งที่ทุกคนลืม: filter นี้มีผลกับรูปที่ถูกเข้ารหัสหลังจากมันเริ่มทำงานเท่านั้น ทุก thumbnail ที่นั่งอยู่ใน wp-content/uploads อยู่แล้วถูกเขียนที่ 82 และจะยังคงอยู่ที่ 82 ต่อไป ต้องสร้างมันใหม่:

wp media regenerate --yes

ถ้าคุณไม่ได้ใช้ WP-CLI ปลั๊กอิน wordpress อย่าง Regenerate Thumbnails ก็ทำสิ่งเดียวกันได้ผ่านหน้า admin

ก่อนจะตัดสินใจเลือกตัวเลข การได้เห็นว่าคุณภาพระดับหนึ่งๆ ทำอะไรกับ รูปของคุณเอง จริงๆ ย่อมดีกว่าการเดา เครื่องมือ WordPress JPEG quality ของเรา จะเข้ารหัสรูปตัวอย่างใหม่ในเบราว์เซอร์ที่ทุกระดับคุณภาพ เพื่อให้คุณเห็น artifact ค่อยๆ ปรากฏขึ้น มันทำให้ความแตกต่างระหว่าง 82, 90 และ 100 บนสกรีนช็อตชัดเจนในแบบที่ตัวเลขในไฟล์ config ไม่มีวันทำได้

วิธีแก้ที่ดีกว่าสำหรับสกรีนช็อตและกราฟิกสีเรียบ

การเพิ่มตัวเลข JPEG เป็นการรักษาที่ปลายเหตุ ปัญหาที่แท้จริงของสกรีนช็อตคือ JPEG เป็น codec ที่ผิดสำหรับมัน การบีบอัดแบบ DCT ของ JPEG ถูกปรับมาสำหรับการไล่เฉดสีแบบภาพถ่าย มันพังทลายเมื่อเจอขอบดำบนขาวที่คมชัดของข้อความและองค์ประกอบ UI ซึ่งนั่นคือเหตุผลว่าทำไมคุณภาพ 82 จึงดูแย่กว่ามากบนสกรีนช็อตเมื่อเทียบกับภาพถ่ายชายหาด

ให้อัพโหลด PNG แทน WordPress จะเก็บไฟล์ย่อย PNG ไว้เป็น PNG และ PNG เป็นแบบ lossless ตัว filter คุณภาพจะไม่แตะมัน จึงไม่มี artifact ที่ขนาดใดๆ เลย มีแต่การ resample ที่สะอาด สำหรับสกรีนช็อต ไฟล์ PNG ที่ปรับให้ดีมักจะ เล็กกว่า JPEG คุณภาพสูงของรูปเดียวกันด้วยซ้ำ เพราะสีเรียบบีบอัดได้ดีใน PNG แต่บีบได้แย่มากใน JPEG

ถ้าคุณต้องการไฟล์ที่เล็กกว่าแบบ lossy โดยไม่มีความเสียหายที่ขอบแบบ JPEG WebP คือทางสายกลาง แต่คุณยังคงควบคุมคุณภาพของมันผ่าน filter wp_editor_set_quality ตัวเดียวกับที่แสดงไว้ข้างบน โดยแยกเงื่อนไขที่ image/webp

สิ่งที่ไม่ควรทำ

อย่าตั้งคุณภาพเป็น 100 แบบทั้งเว็บ นี่คือคำตอบที่ได้โหวตสูงสุดในกระทู้ฟอรัมเกินครึ่ง และมันผิด JPEG คุณภาพ 100 ไม่ใช่ lossless มันยังคง quantize อยู่ แต่มันทำให้ ภาพถ่าย ทุกรูปบนเว็บของคุณบวมขึ้นมากโดยไม่ได้อะไรที่มองเห็นได้กลับมา ฉุด LCP และ Core Web Vitals ให้แย่ลง จงเพิ่มตัวเลขเฉพาะกับ format ที่ต้องการมัน หรือเปลี่ยน format สำหรับกราฟิก อย่าถล่มการบีบอัดทั้งเว็บ

อย่าแก้ไข class-wp-image-editor.php การเปลี่ยนค่าที่ส่งคืนของ get_default_quality() ในโค้ดหลักจะได้ผลจนกว่าจะถึง wp core update ครั้งถัดไปที่จะเขียนทับมันแบบเงียบๆ ให้ใช้ filter แทน

อย่าปิดขนาดรูปเพื่อ “หลบการบีบอัด” การปิดการสร้าง thumbnail ไม่ได้หยุดการเข้ารหัสใหม่ แต่มันทำให้ srcset แบบ responsive พัง ทำให้เบราว์เซอร์ต้องดาวน์โหลดรูปขนาดเต็มของคุณบนมือถือ คนละปัญหา และผลลัพธ์แย่กว่า

อย่าคิดว่าการอัพโหลดใหม่จะแก้รูปเดิมได้ ถ้าคุณตั้ง filter แต่ข้ามขั้นตอน regenerate ไป ไฟล์ย่อยเก่าทุกไฟล์ก็ยังคงเป็นคุณภาพ 82 ตัว filter มองไปข้างหน้าอย่างเดียวเท่านั้น

อย่าโทษไฟล์ -scaled ถ้ารูปของคุณมีขนาดต่ำกว่า 2560px เพราะเส้นทางนั้นไม่เคยรันกับมัน ถ้าคุณ กำลัง เจอปัญหานี้และต้องการให้เสิร์ฟไฟล์ต้นฉบับที่ไม่ถูกแตะต้อง นั่นเป็นการตั้งค่าคนละตัว ดูที่ การปิดใช้ scaled images

ยังติดอยู่ใช่ไหม

ถ้าขนาดใดขนาดหนึ่งยังดูผิดอยู่หลังจาก regenerate แล้ว ให้ยืนยันว่าจริงๆ แล้วหน้าเว็บกำลังเสิร์ฟไฟล์ไหน โดยเปิดรูปในแท็บใหม่แล้วเช็คว่าชื่อไฟล์ลงท้ายด้วยส่วนต่อท้ายที่เป็นขนาด (-1024x768.jpg) หรือ -scaled.jpg image sizes inspector จะแสดงไฟล์ย่อยทุกไฟล์ที่ WordPress สร้างจากการอัพโหลดครั้งเดียว เพื่อให้คุณเห็นได้ชัดๆ ว่า srcset ของคุณกำลังยื่นขนาดไหนให้เบราว์เซอร์ และหยุดไล่แก้ไฟล์ผิดตัวเสียที