3

我们使用 C#.Net 创建了一个自定义验证码生成器。现在我们需要升级我们的验证码以包含音频功能(听验证码)。我们的主要限制是我们不能使用任何第三方组件(Dlls)

请指导我实现这样的功能。

提前致谢。

4

4 回答 4

5

我过去做过这样的事情。需要将背景噪音(器乐)与连接的字母/字符合并。此外,我们从不同的声音中生成了使用中的录音。所有这些都增加了能够从语音中提取验证码文本的“智能代码”的复杂性。由于显而易见的原因,我还必须将最终的 WAVE 转换为 MP3(使用 lame_enc.dll)。向您解释如何做到这一点并不容易,因此我包含了该工作所需的初稿版本,省略了 MP3 代码,因为它使用了第三方 DLL。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Reflection;

namespace suryakiran
{
    internal static partial class Extensions
    {
        internal static byte[] AudioCaptcha(this string text)
        {
            String en = "abcdefghijkoprstvx0123456789", Location = string.Concat(System.Web.HttpContext.Current.Request.ServerVariables["APPL_PHYSICAL_PATH"].ToString(), @"\bin\wav\");
            Int32 dataLength = 0, length = 0, sampleRate = 0, plus = 37500, p = 0;
            Int16 bitsPerSample = 0, channels = 0;
            Byte[] music, wav;
            Random r = new Random();
            p = r.Next(1, 4000000);
            p += (p % 150) + 44;
            Byte[] rb = new Byte[9 * plus];
        // read music
        using (FileStream fs = new FileStream(String.Format(Location + @"z{0}.wav", (r.Next() % 12) + 1), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            wav = new Byte[44];
            fs.Read(wav, 0, 44);
            fs.Position = (long)p;
            fs.Read(rb, 0, rb.Length);
        }
        // make music
        using (MemoryStream ms = new MemoryStream())
        {
            channels = BitConverter.ToInt16(wav, 22); sampleRate = BitConverter.ToInt32(wav, 24); bitsPerSample = BitConverter.ToInt16(wav, 34);
            length = rb.Length; dataLength = rb.Length;
            ms.Write(new Byte[44], 0, 44); ms.Write(rb, 0, rb.Length);
            ms.Position = 0;
            using (BinaryWriter bw = new BinaryWriter(ms))
            {
                bw.Write(new char[4] { 'R', 'I', 'F', 'F' }); bw.Write(length);
                bw.Write(new char[8] { 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' });
                bw.Write((Int32)16); bw.Write((Int16)1);
                bw.Write(channels); bw.Write(sampleRate);
                bw.Write((Int32)(sampleRate * ((bitsPerSample * channels) / 8)));
                bw.Write((Int16)((bitsPerSample * channels) / 8));
                bw.Write(bitsPerSample); bw.Write(new char[4] { 'd', 'a', 't', 'a' }); bw.Write(dataLength);
                music = ms.ToArray();
                p = 0;
            }
        }

        using (MemoryStream final = new MemoryStream())
        {
            final.Write(music, 0, 44);
            // make voice
            using (MemoryStream msvoice = new MemoryStream())
            {
                msvoice.Write(new Byte[plus / 2], 0, plus / 2);
                length += plus; dataLength += plus / 2; p += plus / 2;
                for (var i = 0; i < text.Length; i++)
                {
                    String fn = String.Format(Location + @"{0}\{1}.wav", (r.Next() % 3), en.Substring(en.IndexOf(text.Substring(i, 1)), 1)).Replace("?", "qm");
                    wav = File.ReadAllBytes(fn);
                    Int32 size = BitConverter.ToInt32(wav, 4);
                    {
                        msvoice.Write(new Byte[plus / 2], 0, plus / 2);
                        length += plus; dataLength += plus / 2; p += plus / 2;
                    }
                    msvoice.Write(wav, 44, wav.Length - 44);
                    length += size; dataLength += size - 36;
                }
                msvoice.Position = 0;
                MemoryStream msmusic = new MemoryStream();
                msmusic.Write(music, 0, music.Length);
                msmusic.Position = 44;
                //merge;
                while (final.Length < msmusic.Length)
                    final.WriteByte((byte)(msvoice.ReadByte() - msmusic.ReadByte()));
                return final.ToArray();
            }
        }
    }

    internal static Byte[] VisualCaptcha(this String source)
    {
        try
        {
            Random r = new Random();
            Int32 w = 250, h = 75;
            String family = "Arial Rounded MT Bold";
            using (var bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb))
            {
                Int32 m = 0, nm = 0;
                Color tc;
                using (var g = Graphics.FromImage(bmp))
                {
                    g.TextRenderingHint = TextRenderingHint.AntiAlias;
                    g.SmoothingMode = SmoothingMode.HighQuality;
                    g.Clear(Color.White);
                    SizeF size;
                    m = r.Next() % 9 + 1;
                    nm = r.Next() % 3;
                    tc = Color.FromArgb(255, 255, 255);
                    size = g.MeasureString(source, new Font(family, h * 1.2f, FontStyle.Bold), new SizeF(w * 1F, h * 1F));
                    using (var brush = new LinearGradientBrush(new Rectangle(0, 0, w, h), Color.Black, Color.Black, 45, false))
                    {
                        ColorBlend blend = new ColorBlend(6);
                        for (var i = 0; i < 6; i++) { blend.Positions[i] = i * (1 / 5F); blend.Colors[i] = r.RandomColor(255, 64, 128); }
                        brush.InterpolationColors = blend;

                        for (int wave = 0; wave < 2; wave++)
                        {
                            Int32 min = (15 + wave * 20);
                            PointF[] pt = new PointF[] { new PointF(16f, (float)r.Next(min, min + 10)), new PointF(240f, (float)r.Next(min + 10, min + 20)) };
                            List<PointF> PointList = new List<PointF>();
                            float curDist = 0, distance = 0;
                            for (int i = 0; i < pt.Length - 1; i++)
                            {
                                PointF ptA = pt[i], ptB = pt[i + 1];
                                float deltaX = ptB.X - ptA.X, deltaY = ptB.Y - ptA.Y;
                                curDist = 0;
                                distance = (float)Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
                                while (curDist < distance)
                                {
                                    curDist++;
                                    float offsetX = (float)((double)curDist / (double)distance * (double)deltaX);
                                    float offsetY = (float)((double)curDist / (double)distance * (double)deltaY);
                                    PointList.Add(new PointF(ptA.X + offsetX, ptA.Y + offsetY));
                                }
                            }
                            for (int i = 0; i < PointList.Count - 24; i = i + 24)
                            {
                                float x1 = PointList[i].X, y1 = PointList[i].Y, x2 = PointList[i + 24].X, y2 = PointList[i + 24].Y;
                                float angle = (float)((Math.Atan2(y2 - y1, x2 - x1) * 180 / 3.14159265));
                                g.TranslateTransform(x1, y1);
                                g.RotateTransform(angle);
                                Int32 pm = r.Next() % 2 + 1;
                                Point[] p1 = new Point[] { new Point(0, 0), new Point(3, -3 * pm), new Point(6, -4 * pm), new Point(9, -3 * pm), new Point(12, 0), new Point(15, 3 * pm), new Point(18, 4 * pm), new Point(21, 3 * pm), new Point(24, 0) };
                                using (var path = new GraphicsPath()) g.DrawLines(new Pen(brush, 2f), p1);
                                g.RotateTransform(-angle);
                                g.TranslateTransform(-x1, -y1);
                            }
                        }
                        using (var path = new GraphicsPath())
                        {
                            PointF[] points = new PointF[] { };
                            if (m == 1 || m == 2 || m == 3) // star trek inverse
                            {
                                path.AddString(source, new FontFamily(family), 1, h * 0.75F, new PointF((w - size.Width) / 2F, (h * 0.9F - size.Height) / 2F), StringFormat.GenericTypographic);
                                points = new PointF[] { new PointF(0, 0), new PointF(w, 0), new PointF(w * 0.2F, h), new PointF(w * 0.8F, h) };
                            }
                            else if (m == 4 || m == 5) // star trek
                            {
                                path.AddString(source, new FontFamily(family), 1, h * 0.75F, new PointF((w - size.Width) / 2F, (h * 1.2F - size.Height) / 2F + 2F), StringFormat.GenericTypographic);
                                points = new PointF[] { new PointF(w * 0.2F, 0), new PointF(w * 0.8F, 0), new PointF(0, h), new PointF(w, h) };
                            }
                            else if (m == 6 || m == 7) // grow from left
                            {
                                path.AddString(source, new FontFamily(family), 1, h * 0.75F, new PointF((w * 1.15F - size.Width) / 2F, (h - size.Height) / 2F), StringFormat.GenericTypographic);
                                points = new PointF[] { new PointF(0, h * 0.25F), new PointF(w, 0), new PointF(0, h * 0.75F), new PointF(w, h) };
                            }
                            else if (m == 8 || m == 9) // grow from right
                            {
                                path.AddString(source, new FontFamily(family), 1, h * 0.75F, new PointF((w * 0.85F - size.Width) / 2F, (h - size.Height) / 2F), StringFormat.GenericTypographic);
                                points = new PointF[] { new PointF(w * 0.1F, 0), new PointF(w * 0.9F, h * 0.25F), new PointF(w * 0.1F, h), new PointF(w * 0.9F, h * 0.75F) };
                            }
                            path.Warp(points, new RectangleF(0, 0, w, h));
                            g.FillPath(Brushes.White, path);
                            g.DrawPath(new Pen(brush, 2F), path);
                        }
                    }
                }
                using (var thumb = new Bitmap(128, 40, PixelFormat.Format32bppArgb))
                {
                    using (var g = Graphics.FromImage(thumb))
                    {
                        g.CompositingQuality = CompositingQuality.HighQuality;
                        g.SmoothingMode = SmoothingMode.HighQuality;
                        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        Rectangle tr = new Rectangle(0, 0, thumb.Width, thumb.Height);
                        g.DrawImage(bmp, tr);
                        g.DrawRectangle(new Pen(Brushes.White), new Rectangle(0, 0, 127, 39));
                    }
                    using (var ms = new MemoryStream())
                    {
                        ((Image)thumb).Save(ms, ImageFormat.Png);
                        return ms.ToArray();
                    }
                }
            }
        }
        catch { return null; }
    }

    private static Color RandomColor(this Random rnd, Int32 alpha, Int32 min, Int32 max)
    {
        return Color.FromArgb(alpha, rnd.Next(min, max), rnd.Next(min, max), rnd.Next(min, max));
    }
}

}

对于视觉和音频生成,代码独立于验证码生成(源文本)。WAVE 格式的每个字母音频文件大约为 30KB,但每个音乐文件至少为 8MB,这为我提供了随机起始位置的广泛范围(避免 - 经常 - 重复模式)。您可以将代码编译为 dll 或将其(修改)放在代码隐藏文件中。

于 2012-06-04T05:21:02.173 回答
1

这个想法很简单。

对于您拥有的每个角色,您都将其录制成音频,然后按照与显示角色相同的顺序简单地播放录制的音频。

因此,对于字符 A,您将其拼写并记录在一个 wav 文件中,当您听到它清晰而响亮地说出“alpha”时。

阅读此答案以了解如何播放它们:如何在网络浏览器中播放音频和视频文件?

对后面的代码进行声音编辑

如果您想将音频连接到一个文件,您可以选择一个包含所有现成功能的音频库来进行此混音。

我知道最好的之一是 http://www.un4seen.com/ 上的低音音频

当然,因为您只需要添加两个音频,也许您可​​以直接使用windows 媒体库,检查它的 SDK http://msdn.microsoft.com/en-us/library/windows/desktop/dd757738(v =vs.85).aspx

Windows 媒体已准备好使用 Windows 内的功能,这些功能与音频等媒体有关。

于 2012-05-28T11:14:33.160 回答
1

我将跳过很多验证码安全性和生成细节来支持我的建议。

我在服务器端使用了语音合成器。

在您的 Web 应用程序项目中,添加对System.Speech.dll的引用

我创建了两个页面:

Start.aspx -> 用户将看到的页面。有了这两个元素:

<span id="lblCaptchaText" runat="server"></span>
<embed height="50px" width="100px" src="AudioPlayer.aspx" /> 

后面的代码应该是这样的(简化)

public partial class Start : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // You generate your captcha somehow 
        string captchaString = "E F G 6 7";

        // Let's store the captcha in a place so that our wave generator can find it.
        Session.Add("captcha", captchaString);

        // Display the captcha in the page (this should actually be a blurry non-human readable image)
        lblCaptchaText.InnerText = captchaString;
    }
}

AudioPlayer.aspx -> 生成波形输出给用户的页面。HTML 方面没有任何内容。

public partial class AudioPlayer : System.Web.UI.Page
{
    protected override void OnPreRender(EventArgs e)
    {
        Response.ContentType = "audio/wav";
        MemoryStream mstream = GetAudio(Session["captcha"] as string);
        mstream.Position = 0;            
        mstream.CopyTo(Response.OutputStream);
        Response.End();
    }

    public static MemoryStream GetAudio(string input)
    {
        MemoryStream mem = new MemoryStream();

        Thread t = new Thread(new ThreadStart(() =>
        {
            SpeechSynthesizer synth = new SpeechSynthesizer();

            synth.Rate = -5;
            synth.SetOutputToWaveStream(mem);
            synth.Speak(input);

        }));

        t.Start();
        t.Join();

        return mem;
    }
}

编译您的网站并运行 Start.aspx,您将听到您的验证码。

使用 Microsoft Speech SDK、性能、资源管理、可扩展性等时需要考虑很多事情。好消息是,他们的语音识别引擎和语音合成器具有称为 Microsoft Speech Server 的服务器“风格”。

于 2012-06-08T02:44:22.197 回答
-1

RECAPTHA 默认有这个,那么你为什么要重新发明轮子呢?

http://blog.recaptcha.net/2008/12/new-audio-recaptcha.html

于 2012-06-03T18:02:18.870 回答