Blog (22)
Komentarze (619)
Recenzje (0)

Mową w monitor!

@alucosoftwareMową w monitor!14.12.2012 07:37

W przypadku projektów programistycznych konsekwencją wyboru takiego, a nie innego rozwiązania danego problemu może być nawet, w skrajnej sytuacji, brak możliwości jego sprawnego rozwijania w przyszłości. Wszystko jednak zależy od wzajemnych powiązań składników systemu, ich wewnętrznej budowy i trudu pracy (w tym doświadczenia) włożonych w ich utworzenie. Niemniej bardzo często słyszy się, że wiele elementów aplikacji X w wersji Y.Z zostało przepisanych na nowo, ponieważ programiści stanęli przed murem nie do pokonania (BTW, nadmierne przywiązanie rynku do terminu "deadline" jest przyczynkiem do takiego stanu rzeczy).

Przypadków zupełnie odwrotnych jest proporcjonalnie mniej, w zasadzie odwrotnie proporcjonalnie... To ciekawe, ale tworzenie oprogramowania wykraczającego poza obowiązujące standardy, w sytuacji, w której nad głową nie widać śladu przysłowiowego bata jest zajęciem o wiele bardziej kreatywnym i efektywnym. Większą bowiem wagę przykłada wtedy autor do jakości w funkcji czasu (nawet kosztem pierwszych wrażeń z użytkowania aplikacji) niż efektownej reklamy w krótkiej perspektywie.

Proste, acz gruntownie przemyślane, implementacje pewnych mechanizmów zwykle bardzo rzadko przynoszą rewolucyjne rezultaty, ale nie w przypadku projektu Spikit.

Wynik wdrożenia w życie prostych (z pozoru) pomysłów zaczyna powoli odbijać się szerokim echem. Tu problemem jest nie tyle brak, co prawdziwy nadmiar możliwości oferowanych przez aplikację. Wszystko to za sprawą odmiennego, od powszechne znanego, podejścia w traktowaniu użytkownika. Spikit traktuje użytkownika jak istotę myślącą, nie zaś jak konsumenta przynależącego do szarej masy.

Osobom, którym nazwa aplikacji nie mówi zbyt wiele przypomnę tylko, że jest to program komputerowy, który rozpoznaje mowę i reaguje na wypowiadane przez użytkownika polecenia głosowe.

Dokładny opis aplikacji można znaleźć w bazie dobrychprogramów pod tym adresem.

Świadomy użytkownik

- Czy za pomocą programu mogę yyhh? - Tak. - Nie określiłem czynności. - Nie musiałeś...

Zasadniczą różnicą pomiędzy omawianym oprogramowaniem, a innymi rozwiązaniami tego typu (jeżeli w ogóle możemy mówić o konkurencyjności rozwiązań) jest fakt nieograniczonych możliwości tego pierwszego. Przynajmniej w aspekcie wykorzystania własnej mowy na potrzeby sterowania systemem operacyjnym. W rzeczywistości jednak możliwości programu są ściśle skorelowane z umiejętnościami jego użytkownika, przy czym im większa jest wiedza użytkownika komputera - tym więcej oferuje aplikacja (i odwrotnie).

Ten dość niecodzienny stan rzeczy jest efektem wprowadzonego w Spikit prostego mechanizmu Reguł głosowych, których w pełni świadome stosowanie może zdziałać niejedne cuda na ekranie naszego monitora. Z drugiej strony, nieumiejętność wykorzystania potencjału drzemiącego w tychże Regułach ogranicza drastycznie możliwości użycia aplikacji.

Reguły gry

Zapis dowolnej Reguły głosowej w Spikit jest połączeniem wyodrębnionej, niewielkiej ilości znaków specjalnych (sztuk 9: ( ) | [ ] { } * #), dowolnej ilości słów i zwrotów wykorzystywanych przez użytkownika oraz kilku prostych zasad określających możliwość mieszania jednych (znaków) z drugimi (słowami) w zależności od sytuacji.

Przykład rozbudowanego zapisu wielu poleceń głosowych uwzględniający złożoność mechanizmu Reguł głosowych może początkowo odstraszać (choć zupełnie niepotrzebnie).


[wykonaj] (pierwsze {5} | drugie {1} | trzecie {8}) * polecenie [głosowe]

Pod powyższym zapisem kryją się jednak 3 proste polecenia, aczkolwiek w 24 rozmaitych odmianach, każdej dla innego stanu psychfizycznego osoby je wypowiadającej. Zasadniczo treść komendy głosowej można sprowadzić do postaci:


co mówię {czego oczekuję}

Z logicznego punktu widzenia jest to jak najbardziej poprawne podejście. W naszym codziennym życiu ubieramy myśli w słowa, aby osiągnąć przyświecający tej myśli cel. Słowa same w sobie pełnią rolę czysto symboliczną, są tylko nośnikami skojarzeń, aktywatorami innych oczekiwanych procesów.

Mechanizm Reguł głosowych w Spikit jest właśnie tym, co łączy skojarzenia z czynnościami możliwymi do wykonania za pomocą komputera, dając przy tym użytkownikowi prawdziwą swobodę wyrażania myśli. Każdy z Nas jest bowiem inny, każdy ma własne upodobania, sposób wysławiania się, przyzwyczajenia.

Potęga prostoty

Ten prosty pomysł na stworzenie takiej, a nie innej formy zapisu poleceń głosowych w ramach technologii rozpoznawania mowy dyskretnej daje w rezultacie możliwość jednoczesnego wyboru wielu ścieżek rozwoju aplikacji, czasem nawet zbyt wielu... Zaprezentuję tu początek jednej z nich, tej najbardziej wyczekiwanej tj. możliwości dyktowania tekstu.

Niemal połowa przeczytanych lub zasłyszanych wyrazów to tzw. "hapaks legomena", słowa wypowiadane jednokrotnie, w konkretnej sytuacji. Przy całym bogactwie naszego cudownego języka, rzeczywisty zasób wykorzystywanego przez nas na co dzień słownictwa ogranicza się w najlepszym przypadku do kilku tysięcy pojedynczych wyrazów i dłuższych zwrotów. Żonglujemy nimi w miarę sensowny sposób, choć nierzadko też całkiem niepoprawnie - liczymy przy tym na zrozumienie naszej myśli przewodniej przez drugą ze stron dialogu.

Dla Reguły głosowej liczącej wiele setek poleceń głosowych (tu w znaczeniu pojedynczych słów), Spikit dość dokładnie odwzorowywuje wypowiedź użytkownika. Można więc utworzyć niewielki (~2 tyś. wyrazów) i wysoce spersonalizowany Słownik wykorzystywany na potrzeby codziennej komunikacji z drugim człowiekiem i szereg innych, pomniejszych i okazjonalnych Słowników ułatwiających poruszanie się po zakamarkach Internetu za pomocą haseł tematycznych i branżowych terminów. Słowniki takie składać się będą z najczęściej wykorzystywanych wyrazów (określanych mianem list frekwencyjnych), z których niekoniecznie będzie można utworzyć pozbawioną błędów, całkowicie naturalną wypowiedź. W dobie zaawansowanych algorytmów wyszukiwania oferowanych przez wiodące na rynku firmy (mając tu na myśli oczywiście Google oraz Microsoft, ale także mało znanego w Polsce producenta Wolfram Research) oraz dogłębnej znajomości preferencji pojedynczego użytkownika nie jest to już nawet potrzebne.

Problem niskiej jakości komentarzy w Internecie jest już całkiem poważny...
Problem niskiej jakości komentarzy w Internecie jest już całkiem poważny...

Wykorzystanie Spikit jako narzędzia generującego ciągi pytań do wyszukiwarek internetowych lub krótkie wiadomości tekstowe jest już teraz jak najbardziej możliwe i niesłychanie przydatne. Zwłaszcza gdy przypomnimy sobie, że podstawowym celem przyświecającym idei rozpoczęcia prac nad programem była i jest nadal chęć wspomagania osób niepełnosprawnych ruchowo w codziennej pracy z komputerem przez umożliwienie im bezdotykowej pracy nawet na sprzęcie starej daty, praktycznie żadnym kosztem. Nie oznacza to jednak, że Ty lub ja mielibyśmy wyłącznie bezczynnie patrzeć na rozwój Spikit, wręcz przeciwnie. Im większe będzie Twoje i Twoich znajomych zainteresowanie programem, tym szybszy jego rozwój.

Odsyłam więc do źródeł, każde "Lubię to!" to zawsze jakaś forma wsparcia i pomocy: Spikit na Facebooku.

Proste, lecz bardzo przydatne narzędzie.
Proste, lecz bardzo przydatne narzędzie.

Zapalonym programistom przekazuję za to kod źródłowy tej niewielkiej aplikacji pomocniczej (Generatora Słowników, nie Spikit :P) w nadziei, że może ona znaleźć zastosowanie także w innej dziedzinie wymagającej szybkiej ekstrakcji danych (.NET Framework 2.0+).


using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace ExtractWords
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FormExtractWords());
        }
    }

    public class FormExtractWords : Form
    {
        public RichTextBox Words { get; set; }
        public TextBox Chars { get; set; }

        public FormExtractWords()
        {
            TableLayoutPanel table = new TableLayoutPanel();
            table.Dock = DockStyle.Fill;

            table.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            table.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            table.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            table.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
            table.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            table.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));

            TableLayoutPanel row = new TableLayoutPanel() { AutoSizeMode = AutoSizeMode.GrowAndShrink, AutoSize = true, Dock = DockStyle.Fill, Margin = new Padding(10, 10, 10, 5) };
            row.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            row.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
            row.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));

            Label label = new Label() { Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft, Padding = new Padding(0), Margin = new Padding(0, 0, 5, 0), AutoSize = true };
            label.Font = new Font("Open Sans", 12, GraphicsUnit.Pixel);
            label.Text = "Znaki oddzielające słowa:";

            TextBox chars = new TextBox() { Dock = DockStyle.Fill, Margin = new Padding(5, 0, 0, 0), AllowDrop = false };
            chars.Font = new Font("Open Sans", 12, GraphicsUnit.Pixel);
            chars.Text = "!@#$%^&*()-_=+[]{};:'\"\\|,.<>/?~` \\t\\r\\n";
            Chars = chars;

            row.Controls.Add(label, 0, 0);
            row.Controls.Add(chars, 1, 0);

            Label labelDrop = new Label() { BackColor = Color.FromArgb(250, 250, 250), TextAlign = ContentAlignment.MiddleCenter, Dock = DockStyle.Fill, Padding = new Padding(20), Margin = new Padding(10, 5, 10, 5), AutoSize = true, AllowDrop = true };
            labelDrop.Font = new Font("Open Sans", 16, GraphicsUnit.Pixel);
            labelDrop.Text = "Przeciągnij w to miejsce\njeden lub więcej plików tekstowych\n(kodowanie ANSI)...";
            labelDrop.Paint += new PaintEventHandler(LabelDrop_Paint);
            labelDrop.DragEnter += new DragEventHandler(LabelDrop_DragEnter);
            labelDrop.DragDrop += new DragEventHandler(LabelDrop_DragDrop);

            Button buttonRead = new Button() { Anchor = AnchorStyles.Left | AnchorStyles.Right, Margin = new Padding(10, 5, 10, 5), Padding = new Padding(10), AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
            buttonRead.Font = new Font("Open Sans", 14, GraphicsUnit.Pixel);
            buttonRead.Text = "Pobierz ze schowka (ANSI)";
            buttonRead.Click += new EventHandler(ButtonRead_Click);

            Button buttonSave = new Button() { Anchor = AnchorStyles.Left | AnchorStyles.Right, Margin = new Padding(10, 5, 10, 10), Padding = new Padding(10), AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
            buttonSave.Font = new Font("Open Sans", 14, GraphicsUnit.Pixel);
            buttonSave.Text = "Zapisz do pliku (ANSI)";
            buttonSave.Click += new EventHandler(ButtonSave_Click);

            RichTextBox words = new RichTextBox() { Dock = DockStyle.Fill, Margin = new Padding(10, 5, 10, 5), AllowDrop = false, BorderStyle = BorderStyle.None };
            words.Font = new Font("Open Sans", 12, GraphicsUnit.Pixel);
            Words = words;

            table.Controls.Add(row, 0, 0);
            table.Controls.Add(labelDrop, 0, 1);
            table.Controls.Add(buttonRead, 0, 2);
            table.Controls.Add(words, 0, 3);
            table.Controls.Add(buttonSave, 0, 4);

            this.Text = "Generator Słowników";
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Size = new System.Drawing.Size(420, 512);
            this.Controls.Add(table);
        }

        void LabelDrop_Paint(object sender, PaintEventArgs e)
        {
            Control control = sender as Control;

            if (control == null)
                return;

            Rectangle rect = new Rectangle(control.ClientRectangle.Location, control.ClientSize);
            rect.Inflate(-4, -4);
            e.Graphics.DrawRectangle(new Pen(Brushes.LightGray) { Width = 2, DashStyle = System.Drawing.Drawing2D.DashStyle.Dash }, rect);
        }

        void LabelDrop_DragDrop(object sender, DragEventArgs e)
        {
            string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];

            if (files != null && files.Length > 0)
                GetWordsFromFiles(files);
        }

        void LabelDrop_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Link : DragDropEffects.None;
        }

        void ConcatSortUpdate(ref List<string> list)
        {
            if (!string.IsNullOrEmpty(Words.Text))
                list.AddRange(Words.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries));

            Words.ResetText();

            /* LINQ -> Distinct */

            list.Sort();
            string temp = null;
            StringBuilder result = new StringBuilder();
            int count = 0;

            foreach (string s in list)
            {
                if (!string.IsNullOrEmpty(temp) && temp == s.ToLower())
                    continue;

                result.Append(s.ToLower() + "\r\n"); // Environment.NewLine
                ++count;
                temp = s.ToLower();
            }
            Words.Text = result.ToString();

            MessageBox.Show(String.Format("Ilość wyrazów w Słowniku:\n{0}", count), "Zakończono operację", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        void ButtonSave_Click(object sender, EventArgs e)
        {
            SaveWordsToFile();
        }

        void SaveWordsToFile()
        {
            using (SaveFileDialog saveDialog = new SaveFileDialog())
            {
                saveDialog.Filter = "Plik tekstowy|*.txt";
                if (saveDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    string file = saveDialog.FileName;
                    try
                    {
                        System.IO.File.WriteAllText(file, Words.Text, Encoding.Default);
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(String.Format("Plik:\n{0}\n\nKomunikat:\n{1}", file, e.Message), "Błąd zapisu", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }
                }
            }
        }

        char[] CustomToCharArray(string value)
        {
            value = value.Replace("\\t", "\t").Replace("\\r", "\r").Replace("\\n", "\n");

            return value.ToCharArray();
        }

        void GetWordsFromFiles(string[] files)
        {
            List<string> result = new List<string>();
            char[] splitChars = CustomToCharArray(Chars.Text);

            foreach (string file in files)
            {
                try
                {
                    string allLines = System.IO.File.ReadAllText(file, Encoding.Default);
                    string[] allWords = allLines.Split(splitChars, StringSplitOptions.RemoveEmptyEntries);

                    result.AddRange(allWords);
                }
                catch (Exception e)
                {
                    MessageBox.Show(String.Format("Plik:\n{0}\n\nKomunikat:\n{1}", file, e.Message), "Błąd odczytu", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
            ConcatSortUpdate(ref result);
        }

        void ButtonRead_Click(object sender, EventArgs e)
        {
            GetWordsFromClipboard();
        }

        void GetWordsFromClipboard()
        {
            if (!Clipboard.ContainsText(TextDataFormat.Text))
                return;

            List<string> result = new List<string>();
            char[] splitChars = CustomToCharArray(Chars.Text);
            string[] allWords = Clipboard.GetText(TextDataFormat.Text).Split(splitChars, StringSplitOptions.RemoveEmptyEntries);

            result.AddRange(allWords);

            ConcatSortUpdate(ref result);
        }
    }
}

Link do pliku wykonywalnego Generatora Słowników

Szanowna Użytkowniczko! Szanowny Użytkowniku!
×
Aby dalej móc dostarczać coraz lepsze materiały redakcyjne i udostępniać coraz lepsze usługi, potrzebujemy zgody na dopasowanie treści marketingowych do Twojego zachowania. Twoje dane są u nas bezpieczne, a zgodę możesz wycofać w każdej chwili na podstronie polityka prywatności.

Kliknij "PRZECHODZĘ DO SERWISU" lub na symbol "X" w górnym rogu tej planszy, jeżeli zgadzasz się na przetwarzanie przez Wirtualną Polskę i naszych Zaufanych Partnerów Twoich danych osobowych, zbieranych w ramach korzystania przez Ciebie z usług, portali i serwisów internetowych Wirtualnej Polski (w tym danych zapisywanych w plikach cookies) w celach marketingowych realizowanych na zlecenie naszych Zaufanych Partnerów. Jeśli nie zgadzasz się na przetwarzanie Twoich danych osobowych skorzystaj z ustawień w polityce prywatności. Zgoda jest dobrowolna i możesz ją w dowolnym momencie wycofać zmieniając ustawienia w polityce prywatności (w której znajdziesz odpowiedzi na wszystkie pytania związane z przetwarzaniem Twoich danych osobowych).

Od 25 maja 2018 roku obowiązuje Rozporządzenie Parlamentu Europejskiego i Rady (UE) 2016/679 (określane jako "RODO"). W związku z tym chcielibyśmy poinformować o przetwarzaniu Twoich danych oraz zasadach, na jakich odbywa się to po dniu 25 maja 2018 roku.

Kto będzie administratorem Twoich danych?

Administratorami Twoich danych będzie Wirtualna Polska Media Spółka Akcyjna z siedzibą w Warszawie, oraz pozostałe spółki z grupy Wirtualna Polska, jak również nasi Zaufani Partnerzy, z którymi stale współpracujemy. Szczegółowe informacje dotyczące administratorów znajdują się w polityce prywatności.

O jakich danych mówimy?

Chodzi o dane osobowe, które są zbierane w ramach korzystania przez Ciebie z naszych usług, portali i serwisów internetowych udostępnianych przez Wirtualną Polskę, w tym zapisywanych w plikach cookies, które są instalowane na naszych stronach przez Wirtualną Polskę oraz naszych Zaufanych Partnerów.

Dlaczego chcemy przetwarzać Twoje dane?

Przetwarzamy je dostarczać coraz lepsze materiały redakcyjne, dopasować ich tematykę do Twoich zainteresowań, tworzyć portale i serwisy internetowe, z których będziesz korzystać z przyjemnością, zapewniać większe bezpieczeństwo usług, udoskonalać nasze usługi i maksymalnie dopasować je do Twoich zainteresowań, pokazywać reklamy dopasowane do Twoich potrzeb. Szczegółowe informacje dotyczące celów przetwarzania Twoich danych znajdują się w polityce prywatności.

Komu możemy przekazać dane?

Twoje dane możemy przekazywać podmiotom przetwarzającym je na nasze zlecenie oraz podmiotom uprawnionym do uzyskania danych na podstawie obowiązującego prawa – oczywiście tylko, gdy wystąpią z żądaniem w oparciu o stosowną podstawę prawną.

Jakie masz prawa w stosunku do Twoich danych?

Masz prawo żądania dostępu, sprostowania, usunięcia lub ograniczenia przetwarzania danych. Możesz wycofać zgodę na przetwarzanie, zgłosić sprzeciw oraz skorzystać z innych praw wymienionych szczegółowo w polityce prywatności.

Jakie są podstawy prawne przetwarzania Twoich danych?

Podstawą prawną przetwarzania Twoich danych w celu świadczenia usług jest niezbędność do wykonania umów o ich świadczenie (tymi umowami są zazwyczaj regulaminy). Podstawą prawną przetwarzania danych w celu pomiarów statystycznych i marketingu własnego administratorów jest tzw. uzasadniony interes administratora. Przetwarzanie Twoich danych w celach marketingowych realizowanych przez Wirtualną Polskę na zlecenie Zaufanych Partnerów i bezpośrednio przez Zaufanych Partnerów będzie odbywać się na podstawie Twojej dobrowolnej zgody.