Echo Data Hiding [สารบัญกลุ่มเรื่องที่กำลังศึกษา] เนื้อหาตอนนี้ผมสรุปจากหัวข้อ Echo data hiding ในบทความ Techniques for data hiding โดย W. Bender, D. Gruhl, N. Morimoto กับ A. Lu (IBM System Journal, Vol. 35, Nos 3 & 4, 1996) Echo data hiding ฝังข้อมูลลงในสัญญาณเสียงต้นฉบับโดยการใส่ echo ข้อมูลถูกซ่อนโดยการปรับพารามิเตอร์ของ echo สามตัว ได้แก่ แอมปลิจูดเริ่มต้น, decay rate (อัตราการลดลง) และ offset (หรือค่าเวลาหน่วง) ดังรูป ![]() ยิ่ง offset ระหว่างสัญญาณต้นฉบับกับ echo ลดลง สัญญาณทั้งคู่ก็จะเริ่มผสมกัน ถึงจุดหนึ่ง หูมนุษย์ก็ไม่สามารถแยกความแตกต่างของสัญญาณทั้งสองได้ ฉะนั้น echo จึงเหมือนกับการทำให้เสียงก้องขึ้น สำหรับคำถามว่า offset มากสุดแค่ไหนถึงเริ่มแยกไม่ได้นั้นขึ้นอยู่กับปัจจัยหลายอย่าง อาทิ คุณภาพของการบันทึกเสียงต้นฉบับ ชนิดของเสียงที่เอามาทำ echo และผู้ฟังเองก็เป็นปัจจัยหนึ่งด้วย โดยทั่วไป เราพบว่าที่ offset เท่ากับ 1 ms เสียงก็เริ่มผสมจนแยกยากแล้วสำหรับผู้ฟังส่วนใหญ่ coder หรือตัวเข้ารหัสใช้เวลาหน่วง 2 ค่า ค่าหนึ่ง (คือ offset) แทนบิต 1, ส่วนอีกค่าหนึ่ง (คือ offset + delta) แทนบิต 0 ซึ่งเวลาหน่วงทั้ง 2 ค่าต่ำกว่าขีดเริ่มที่คนเราจะสามารถแยกความแตกต่างของสองเสียงได้นะครับ นอกเหนือจากการลดค่าเวลาหน่วงแล้ว เรายังปรับแอมปลิจูดเริ่มต้นกับ decay rate ได้ด้วย เพื่อไม่ให้ echo ที่ใส่เข้าไปนั้นถูกรับรู้ได้ เราสามารถมองกระบวนการเข้ารหัส (encoding process) เป็นระบบที่มีฟังก์ชั่นระบบ (system function) ตัวใดตัวหนึ่งในสองตัวที่เป็นไปได้ โดยฟังก์ชั่นระบบในโดเมนเวลาก็คือ discrete time exponential ที่แตกต่างกันเพียงแค่เวลาหน่วงระหว่างอิมพัลซ์ (ดูรูป) ![]() เพื่อความง่าย เราเลือกตัวอย่างที่มีอิมพัลซ์ 2 อิมพัลซ์ (อิมพัลซ์ตัวแรกสำหรับ copy สัญญาณต้นฉบับ และอิมพัลซ์ตัวที่สองสำหรับสร้าง echo) การเพิ่มจำนวนอิมพัลซ์เท่ากับการเพิ่มจำนวน echoes ![]() kernel ดังรูป A ข้างบนนี้แทนฟังก์ชั่นระบบสำหรับการเข้ารหัสบิต 1 และเราใช้ฟังก์ชั่นระบบตามรูป B เพื่อเข้ารหัสบิต 0 การประมวลผลสัญญาณตามรูป A หรือ B ตัวใดตัวหนึ่งจะให้ผลลัพธ์เป็นสัญญาณที่เข้ารหัสแล้วดังรูปด้านล่าง ![]() เวลาหน่วง (δb) ระหว่างสัญญาณต้นฉบับกับ echo ขึ้นอยู่กับว่าเราใช้ kernel อันไหน หรือฟังก์ชั่นระบบอันไหน ถ้าใช้ kernel "บิต 1" ก็จะมีเวลาหน่วงเท่ากับ δ1 ใช้ kernel "บิต 0" เวลาหน่วงเท่ากับ δ0 ในกรณีที่เข้ารหัสมากกว่าหนึ่งบิต จะต้องแบ่งสัญญาณต้นฉบับออกเป็นท่อน ๆ แต่ละท่อนก็ใช้ทำ echo ตามบิตที่ต้องการโดยถือว่าแต่ละท่อนเป็นสัญญาณที่เป็นอิสระจากกัน แล้วรวมแต่ละท่อนเข้าด้วยกันเป็นสัญญาณที่เข้ารหัส (ซึ่งมีหลายบิต) ขั้นสุดท้าย ![]() รูปนี้แสดงตัวอย่างแบ่งสัญญาณออกเป็น 7 ท่อนเท่า ๆ กัน คือ ท่อน a ถึงท่อน g และเราต้องการใส่บิต 1 ลงในท่อน a c d และ g ฉะนั้น เราใช้ kernel "บิต 1" เป็นฟังก์ชั่นระบบสำหรับ 4 ท่อนนี้ แต่ละท่อนจะ convolve กับฟังก์ชั่นระบบตัวใครตัวมัน ส่วนอีก 3 ท่อนที่เหลือจะใช้ kernel "บิต 0" หลังจากที่ทุกท่อน convolve กับฟังก์ชั่นระบบตามบิตที่จะฝังเสร็จเรียบร้อยแล้ว เราจะเอาทั้ง 7 ท่อนมาเรียงต่อรวมกัน วิธีทำเพื่อให้ได้ตาม concept ที่กล่าวมานะครับ ขั้นแรก เราจะเริ่มจากการสร้างสัญญาณ echo "บิต 1" โดยทำการ echo สัญญาณต้นฉบับด้วย kernel "บิต 1" และใช้ kernel "บิต 0" ในการสร้างสัญญาณ echo "บิต 0" ดังรูป (เส้นสีม่วงคือสัญญาณ echo) ![]() ต่อมาเราจะสร้างสัญญาณ mixer สองสัญญาณ (ดูรูปด้านล่าง) เพื่อใช้รวมสัญญาณ echo สองตัวนั้น และสัญญาณ mixer จะเป็น 1 หรือ 0 ก็ขึ้นอยู่กับค่าของบิตที่เราจะฝังลงในแต่ละท่อนของสัญญาณต้นฉบับ ![]() สัญญาณ mixer "บิต 1" จะคูณกับสัญญาณ echo "บิต 1" ขณะที่สัญญาณ mixer "บิต 0" จะคูณกับสัญญาณ echo "บิต 0" แล้วเอาผลลัพธ์ที่ได้มารวมกัน สังเกตว่า สัญญาณ mixer "บิต 1" กับสัญญาณ mixer "บิต 0" จะรวมกันเป็น 1 เสมอ นอกจากนี้ การเปลี่ยนค่าของสัญญาณ mixer จาก 1 เป็น 0 และจาก 0 เป็น 1 จะไม่เป็นการเปลี่ยนแบบทันทีทันใด แต่จะเอียงมีความชัน ทำให้ transition มีความต่อเนื่อง block diagram ขั้นตอนการเข้ารหัสแสดงดังรูป ![]() การดึงข้อมูลที่ถูกฝังออกมาจะต้องตรวจจับระยะห่างระหว่าง echo ทำได้โดยการหาขนาด (magnitude) ของ autocorrelation ของ cepstrum ของสัญญาณที่เข้ารหัส (cepstrum [มาจากการเรียงตัวอักษร 4 ตัวแรกของคำว่า spectrum ใหม่ จากหลังมาหน้า] คือ ผลลัพธ์ที่ได้จากการทำ inverse Fourier transform ของ logarithm ของสเปกตรัมโดยประมาณของสัญญาณ หรือ F-1{log(|F(x)|)}) ต่อไปนี้เป็นตัวอย่างขั้นตอนการถอดรหัส เริ่มด้วยสัญญาณตัวอย่างซึ่งเป็นขบวนของอิมพัลซ์ ที่อิมพัลซ์เหล่านั้นอยู่ห่างกันด้วยช่วงที่กำหนด และมีแอมปลิจูดลดลงแบบ exponential (สัญญาณที่ตำแหน่งอื่นเป็นศูนย์) ดังรูป ![]() ต่อมา หา cepstrum ซึ่งการทำ cepstrum ส่งผลให้ระยะห่างระหว่าง echo กับสัญญาณต้นฉบับชัดขึ้นเล็กน้อย แต่ข้อเสียคือ ผลลัพธ์จาก cepstrum จะสร้าง echo ซ้ำทุก ๆ δ วินาที ![]() นอกจากนี้ ขนาดของอิมพัลซ์ที่เป็น echoes ก็มีขนาดเล็กเมื่อเทียบกับสัญญาณต้นฉบับ มันจึงตรวจจับยาก ทางออกคือ ทำ autocorrelation ของ cepstrum เริ่มด้วยการทำ echo ที่มีเวลาหน่วง δ กับสัญญาณครั้งหนึ่ง โดยใช้ kernel ดังรูป ![]() และได้ผลลัพธ์ ![]() มีเพียงอิมพัลซ์แรกเท่านั้นที่จะถูกขยายใหญ่มากขึ้นอย่างเด่นชัด เพราะมันจะรวมกับอิมพัลซ์ที่อยู่ถัดไป ทำให้เกิด spike ที่ตำแหน่งของอิมพัลซ์แรก ซึ่ง spike นี้จะอยู่ห่างจากสัญญาณต้นฉบับ δ1 หรือ δ0 วินาที ตอนถอดรหัส เราจะบอกว่าข้อมูลที่ฝังคือบิต 1 ถ้าขนาด (magnitude) ของ autocorrelaion function ที่ δ1 วินาที มีค่ามากกว่าเมื่อเทียบกับที่ δ0 วินาที แต่ถ้ากลับกัน คือ ขนาด (magnitude) ของ autocorrelaion function ที่ δ0 วินาที มีค่ามากกว่าเมื่อเทียบกับที่ δ1 วินาที เราก็จะได้บิต 0 วิธีนี้สามารถเข้ารหัสและถอดรหัสข้อมูลบิตลงเสียงโดยทำให้มีการเปลี่ยนแปลงน้อยที่สุด (หมายถึง แยกความแตกต่างโดยการฟัง ระหว่างเสียงก่อนและหลังเข้ารหัสไม่ได้) ได้ประมาณ 16 bps ![]() |
บทความทั้งหมด
|