From b6d1d30d105cacd6cfcdc02b7cce66118be520f0 Mon Sep 17 00:00:00 2001 From: Ogoun Date: Fri, 21 Jul 2023 01:11:59 +0300 Subject: [PATCH] Fixes --- TestApp/AppSettings.cs | 12 + .../Architectures/YoloV5/Yolov5Detector.cs | 1 - .../Architectures/YoloV8/Yolov8Detector.cs | 249 ++++++++++++++++++ ZeroLevel.NN/Services/ImagePreprocessor.cs | 6 +- ZeroLevel.NN/Services/SSDNN.cs | 5 +- ZeroLevel.NN/ZeroLevel.NN.csproj | 6 +- .../PartitionStorage/Interfaces/IStore.cs | 3 +- ZeroLevel/Services/PartitionStorage/Store.cs | 4 +- .../Services/Semantic/Helpers/StopWords.cs | 2 +- 9 files changed, 272 insertions(+), 16 deletions(-) create mode 100644 TestApp/AppSettings.cs create mode 100644 ZeroLevel.NN/Architectures/YoloV8/Yolov8Detector.cs diff --git a/TestApp/AppSettings.cs b/TestApp/AppSettings.cs new file mode 100644 index 0000000..86f297f --- /dev/null +++ b/TestApp/AppSettings.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TestApp +{ + internal class AppSettings + { + } +} diff --git a/ZeroLevel.NN/Architectures/YoloV5/Yolov5Detector.cs b/ZeroLevel.NN/Architectures/YoloV5/Yolov5Detector.cs index 50a2733..6798591 100644 --- a/ZeroLevel.NN/Architectures/YoloV5/Yolov5Detector.cs +++ b/ZeroLevel.NN/Architectures/YoloV5/Yolov5Detector.cs @@ -1,5 +1,4 @@ using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; using ZeroLevel.NN.Models; namespace ZeroLevel.NN.Architectures.YoloV5 diff --git a/ZeroLevel.NN/Architectures/YoloV8/Yolov8Detector.cs b/ZeroLevel.NN/Architectures/YoloV8/Yolov8Detector.cs new file mode 100644 index 0000000..5a07a99 --- /dev/null +++ b/ZeroLevel.NN/Architectures/YoloV8/Yolov8Detector.cs @@ -0,0 +1,249 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Diagnostics; +using ZeroLevel.NN.Models; + +namespace ZeroLevel.NN.Architectures.YoloV5 +{ + public sealed class TensorPoolItem + { + public int StartX; + public int StartY; + public int Width; + public int Height; + public int TensorIndex; + public Tensor Tensor; + + public TensorPoolItem(Tensor tensor, int tensorIndex, int startX, int startY, int width, int height) + { + Tensor = tensor; + TensorIndex = tensorIndex; + StartX = startX; + StartY = startY; + Width = width; + Height = height; + } + + public void Set(int x, int y, float valueR, float valueG, float valueB) + { + var tx = x - StartX; + if (tx < 0 || tx >= Width) return; + var ty = y - StartY; + + this.Tensor[TensorIndex, 0, tx, ty] = valueR; + this.Tensor[TensorIndex, 1, tx, ty] = valueG; + this.Tensor[TensorIndex, 2, tx, ty] = valueB; + } + } + + public sealed class TensorPool + { + public Tensor[] Tensors; + private const int MAX_BATCH_SIZE = 16; + + public readonly int CropHeight; + public readonly int CropWidth; + public readonly int Width; + public readonly int Height; + + private readonly SortedDictionary _pool = new SortedDictionary(); + private readonly Dictionary> _batchPool = new Dictionary>(); + + public TensorPool(int fullWidth, int fullHeight, int cropWidth, int cropHeight, bool overlap, float overlapProportion) + { + Width = fullWidth; + Height = fullHeight; + CropHeight = cropHeight; + var x_points = SplitRange(fullWidth, cropWidth, overlap ? overlapProportion : 0).ToArray(); + var y_points = SplitRange(fullHeight, cropHeight, overlap ? overlapProportion : 0).ToArray(); + var total = x_points.Length * y_points.Length; + int offset = total % MAX_BATCH_SIZE; + int count_tensor_batches = total / MAX_BATCH_SIZE + (offset == 0 ? 0 : 1); + + Tensors = new Tensor[count_tensor_batches]; + for (int batch_index = 0; batch_index < count_tensor_batches; batch_index++) + { + if (batch_index < count_tensor_batches - 1) + { + Tensors[batch_index] = new DenseTensor(new[] { MAX_BATCH_SIZE, 3, cropWidth, cropHeight }); + } + else + { + Tensors[batch_index] = new DenseTensor(new[] { offset == 0 ? MAX_BATCH_SIZE : offset, 3, cropWidth, cropHeight }); + } + } + var batchIndex = 0; + var tensorIndex = 0; + _batchPool[batchIndex] = new List(MAX_BATCH_SIZE); + foreach (var y in y_points) + { + var columnPool = new TensorPoolItem[x_points.Length]; + int column = 0; + foreach (var x in x_points) + { + columnPool[column] = new TensorPoolItem(Tensors[batchIndex], tensorIndex, x, y, cropWidth, cropHeight); + _batchPool[batchIndex].Add(columnPool[column]); + column++; + tensorIndex++; + if (tensorIndex >= MAX_BATCH_SIZE) + { + tensorIndex = 0; + batchIndex++; + _batchPool[batchIndex] = new List(MAX_BATCH_SIZE); + } + } + _pool[y] = columnPool; + } + VirtualPool = new List(x_points.Length * 2); + } + + public TensorPoolItem GetTensor(int batchIndex, int tensorIndex) + { + foreach (var item in _batchPool[batchIndex]) + { + if (item.TensorIndex == tensorIndex) return item; + } + throw new InvalidProgramException(); + } + + public IEnumerable SplitRange(int size, int cropSize, float overlapProportion) + { + var stride = (int)(cropSize * (1f - overlapProportion)); + var counter = 0; + while (true) + { + var pt = stride * counter; + if ((pt + cropSize) > size) + { + if (cropSize == size || pt == size) + { + break; + } + yield return size - cropSize; + break; + } + else + { + yield return pt; + } + counter++; + } + } + + private readonly List VirtualPool; + public List GetSubpoolForY(int y) + { + VirtualPool.Clear(); + foreach (var pair in _pool) + { + if (y >= pair.Key && y < (pair.Key + CropHeight)) + { + VirtualPool.AddRange(pair.Value); + } + } + return VirtualPool; + } + } + + public class Yolov8Detector + : SSDNN + { + private int INPUT_WIDTH = 1280; + private int INPUT_HEIGHT = 1280; + + public Yolov8Detector(string modelPath, int inputWidth = 1280, int inputHeight = 1280, bool gpu = false) + : base(modelPath, gpu) + { + INPUT_HEIGHT = inputHeight; + INPUT_WIDTH = inputWidth; + } + public List Predict(Image image, float threshold) + { + var sw = new Stopwatch(); + sw.Start(); + var input = ImageToTensors(image); + sw.Stop(); + return Predict(input, threshold); + } + public List Predict(TensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / (float)inputs.Width; + var relative_koef_y = 1.0f / (float)inputs.Height; + int batchIndex = 0; + foreach (var input in inputs.Tensors) + { + Extract(new Dictionary> { { "images", input } }, d => + { + Tensor output; + if (d.ContainsKey("output0")) + { + output = d["output0"]; + } + else + { + output = d.First().Value; + } + if (output != null && output != null) + { + for (int tensorIndex = 0; tensorIndex < output.Dimensions[0]; tensorIndex++) + { + var tensor = inputs.GetTensor(batchIndex, tensorIndex); + for (int box = 0; box < output.Dimensions[2]; box++) + { + var conf = output[tensorIndex, 4, box]; // уверенность в наличии любого объекта + if (conf > threshold) + { + // Перевод относительно входа модели в относительные координаты + var cx = output[tensorIndex, 1, box]; + var cy = output[tensorIndex, 0, box]; + var w = output[tensorIndex, 3, box]; + var h = output[tensorIndex, 2, box]; + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + }); + batchIndex++; + } + return result; + } + + private TensorPool ImageToTensors(Image image) + { + var pool = new TensorPool(image.Width, image.Height, INPUT_WIDTH, INPUT_HEIGHT, true, 0); + var koef = 1.0f / 255.0f; + ((Image)image).ProcessPixelRows(pixels => + { + for (int y = 0; y < pixels.Height; y++) + { + var subpool = pool.GetSubpoolForY(y); + Span pixelSpan = pixels.GetRowSpan(y); + for (int x = 0; x < pixels.Width; x++) + { + float r = koef * (float)pixelSpan[x].R; + float g = koef * (float)pixelSpan[x].G; + float b = koef * (float)pixelSpan[x].B; + for (int i = 0; i < subpool.Count; i++) + { + subpool[i].Set(x, y, r, g, b); + } + } + } + }); + return pool; + } + } +} diff --git a/ZeroLevel.NN/Services/ImagePreprocessor.cs b/ZeroLevel.NN/Services/ImagePreprocessor.cs index 1559dca..5823ce3 100644 --- a/ZeroLevel.NN/Services/ImagePreprocessor.cs +++ b/ZeroLevel.NN/Services/ImagePreprocessor.cs @@ -1,7 +1,5 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; +using Aurigma.GraphicsMill; +using Microsoft.ML.OnnxRuntime.Tensors; using ZeroLevel.NN.Models; namespace ZeroLevel.NN diff --git a/ZeroLevel.NN/Services/SSDNN.cs b/ZeroLevel.NN/Services/SSDNN.cs index 484ae98..8ed3cac 100644 --- a/ZeroLevel.NN/Services/SSDNN.cs +++ b/ZeroLevel.NN/Services/SSDNN.cs @@ -1,6 +1,5 @@ using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; using ZeroLevel.NN.Models; namespace ZeroLevel.NN @@ -17,8 +16,8 @@ namespace ZeroLevel.NN try { var so = SessionOptions.MakeSessionOptionWithCudaProvider(0); - so.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_VERBOSE; - so.GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL; + so.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_FATAL; + so.GraphOptimizationLevel = GraphOptimizationLevel.ORT_DISABLE_ALL; _session = new InferenceSession(modelPath, so); } catch (Exception ex) diff --git a/ZeroLevel.NN/ZeroLevel.NN.csproj b/ZeroLevel.NN/ZeroLevel.NN.csproj index a0b8bf9..6ef8a8a 100644 --- a/ZeroLevel.NN/ZeroLevel.NN.csproj +++ b/ZeroLevel.NN/ZeroLevel.NN.csproj @@ -35,9 +35,9 @@ - - - + + + diff --git a/ZeroLevel/Services/PartitionStorage/Interfaces/IStore.cs b/ZeroLevel/Services/PartitionStorage/Interfaces/IStore.cs index 995e25f..6121dc4 100644 --- a/ZeroLevel/Services/PartitionStorage/Interfaces/IStore.cs +++ b/ZeroLevel/Services/PartitionStorage/Interfaces/IStore.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using ZeroLevel.Services.PartitionStorage.Interfaces; namespace ZeroLevel.Services.PartitionStorage @@ -29,7 +28,7 @@ namespace ZeroLevel.Services.PartitionStorage /// /// Performs a search for data in the repository /// - Task> Search(StoreSearchRequest searchRequest); + StoreSearchResult Search(StoreSearchRequest searchRequest); /// /// bypass all key value by meta /// diff --git a/ZeroLevel/Services/PartitionStorage/Store.cs b/ZeroLevel/Services/PartitionStorage/Store.cs index 796377a..c4883aa 100644 --- a/ZeroLevel/Services/PartitionStorage/Store.cs +++ b/ZeroLevel/Services/PartitionStorage/Store.cs @@ -64,7 +64,7 @@ namespace ZeroLevel.Services.PartitionStorage _fileAccessorCachee.DropAllIndexReaders(); } - public async Task> Search(StoreSearchRequest searchRequest) + public StoreSearchResult Search(StoreSearchRequest searchRequest) { var result = new StoreSearchResult(); var results = new ConcurrentDictionary>>(); @@ -77,7 +77,7 @@ namespace ZeroLevel.Services.PartitionStorage { MaxDegreeOfParallelism = _options.MaxDegreeOfParallelism }; - await Parallel.ForEachAsync(partitionsSearchInfo, options, async (pair, _) => + Parallel.ForEach(partitionsSearchInfo, options, (pair, _) => { using (var accessor = CreateAccessor(pair.Key)) { diff --git a/ZeroLevel/Services/Semantic/Helpers/StopWords.cs b/ZeroLevel/Services/Semantic/Helpers/StopWords.cs index 157edb5..d62cd22 100644 --- a/ZeroLevel/Services/Semantic/Helpers/StopWords.cs +++ b/ZeroLevel/Services/Semantic/Helpers/StopWords.cs @@ -4,7 +4,7 @@ namespace ZeroLevel.Implementation.Semantic.Helpers { public static class StopWords { - private readonly static HashSet _stop_words = new HashSet { "a", "about", "all", "am", "an", "and", "any", "are", "as", "at", "be", "been", "but", "by", "can", "could", "do", "for", "from", "has", "have", "i", "if", "in", "is", "it", "me", "my", "no", "not", "of", "on", "one", "or", "so", "that", "the", "them", "there", "they", "this", "to", "was", "we", "what", "which", "will", "with", "would", "you", "а", "будем", "будет", "будете", "будешь", "буду", "будут", "будучи", "будь", "будьте", "бы", "был", "была", "были", "было", "быть", "в", "вам", "вами", "вас", "весь", "во", "вот", "все", "всё", "всего", "всей", "всем", "всём", "всеми", "всему", "всех", "всею", "всея", "всю", "вся", "вы", "да", "для", "до", "его", "едим", "едят", "ее", "её", "ей", "ел", "ела", "ем", "ему", "емъ", "если", "ест", "есть", "ешь", "еще", "ещё", "ею", "же", "за", "и", "из", "или", "им", "ими", "имъ", "их", "к", "как", "кем", "ко", "когда", "кого", "ком", "кому", "комья", "которая", "которого", "которое", "которой", "котором", "которому", "которою", "которую", "которые", "который", "которым", "которыми", "которых", "кто", "меня", "мне", "мной", "мною", "мог", "моги", "могите", "могла", "могли", "могло", "могу", "могут", "мое", "моё", "моего", "моей", "моем", "моём", "моему", "моею", "можем", "может", "можете", "можешь", "мои", "мой", "моим", "моими", "моих", "мочь", "мою", "моя", "мы", "на", "нам", "нами", "нас", "наса", "наш", "наша", "наше", "нашего", "нашей", "нашем", "нашему", "нашею", "наши", "нашим", "нашими", "наших", "нашу", "не", "него", "нее", "неё", "ней", "нем", "нём", "нему", "нет", "нею", "ним", "ними", "них", "но", "о", "об", "один", "одна", "одни", "одним", "одними", "одних", "одно", "одного", "одной", "одном", "одному", "одною", "одну", "он", "она", "оне", "они", "оно", "от", "по", "при", "с", "сам", "сама", "сами", "самим", "самими", "самих", "само", "самого", "самом", "самому", "саму", "свое", "своё", "своего", "своей", "своем", "своём", "своему", "своею", "свои", "свой", "своим", "своими", "своих", "свою", "своя", "себе", "себя", "собой", "собою", "та", "так", "такая", "такие", "таким", "такими", "таких", "такого", "такое", "такой", "таком", "такому", "такою", "такую", "те", "тебе", "тебя", "тем", "теми", "тех", "то", "тобой", "тобою", "того", "той", "только", "том", "томах", "тому", "тот", "тою", "ту", "ты", "у", "уже", "чего", "чем", "чём", "чему", "что", "чтобы", "эта", "эти", "этим", "этими", "этих", "это", "этого", "этой", "этом", "этому", "этот", "этою", "эту", "я", "ещë", "еë", "моë", "моëм", "всë", "кто-то ", "что-то", "мені", "наші", "нашої", "нашій", "нашою", "нашім", "ті", "тієї", "тією", "тії", "теє" }; + private readonly static HashSet _stop_words = new HashSet { "a", "about", "all", "am", "an", "and", "any", "are", "as", "at", "be", "been", "but", "by", "can", "could", "do", "for", "from", "has", "have", "i", "if", "in", "is", "it", "me", "my", "no", "not", "of", "on", "one", "or", "so", "that", "the", "them", "there", "they", "this", "to", "was", "we", "what", "which", "will", "with", "would", "you", "а", "будем", "будет", "будете", "будешь", "буду", "будут", "будучи", "будь", "будьте", "бы", "был", "была", "были", "было", "быть", "в", "вам", "вами", "вас", "весь", "во", "вот", "все", "всё", "всего", "всей", "всем", "всём", "всеми", "всему", "всех", "всею", "всея", "всю", "вся", "вы", "да", "для", "до", "его", "едим", "едят", "ее", "её", "ей", "ел", "ела", "ем", "ему", "емъ", "если", "ест", "есть", "ешь", "еще", "ещё", "ею", "же", "за", "и", "из", "или", "им", "ими", "имъ", "их", "к", "как", "кем", "ко", "когда", "кого", "ком", "кому", "комья", "которая", "которого", "которое", "которой", "котором", "которому", "которою", "которую", "которые", "который", "которым", "которыми", "которых", "кто", "меня", "мне", "мной", "мною", "мог", "моги", "могите", "могла", "могли", "могло", "могу", "могут", "мое", "моё", "моего", "моей", "моем", "моём", "моему", "моею", "можем", "может", "можете", "можешь", "мои", "мой", "моим", "моими", "моих", "мочь", "мою", "моя", "мы", "на", "нам", "нами", "нас", "наса", "наш", "наша", "наше", "нашего", "нашей", "нашем", "нашему", "нашею", "наши", "нашим", "нашими", "наших", "нашу", "не", "него", "нее", "неё", "ней", "нем", "нём", "нему", "нет", "нею", "ним", "ними", "них", "но", "о", "об", "один", "одна", "одни", "одним", "одними", "одних", "одно", "одного", "одной", "одном", "одному", "одною", "одну", "ой", "он", "она", "оне", "они", "оно", "от", "по", "при", "с", "сам", "сама", "сами", "самим", "самими", "самих", "само", "самого", "самом", "самому", "саму", "свое", "своё", "своего", "своей", "своем", "своём", "своему", "своею", "свои", "свой", "своим", "своими", "своих", "свою", "своя", "себе", "себя", "собой", "собою", "та", "так", "такая", "такие", "таким", "такими", "таких", "такого", "такое", "такой", "таком", "такому", "такою", "такую", "те", "тебе", "тебя", "тем", "теми", "тех", "то", "тобой", "тобою", "того", "той", "только", "том", "томах", "тому", "тот", "тою", "ту", "ты", "у", "уже", "чего", "чем", "чём", "чему", "что", "чтобы", "эта", "эти", "этим", "этими", "этих", "это", "этого", "этой", "этом", "этому", "этот", "этою", "эту", "я", "ещë", "еë", "моë", "моëм", "всë", "кто-то ", "что-то", "мені", "наші", "нашої", "нашій", "нашою", "нашім", "ті", "тієї", "тією", "тії", "теє" }; private readonly static HashSet _html_tags = new HashSet { "doctype", "a", "accesskey", "charset", "coords", "download", "href", "hreflang", "name", "rel", "rev", "shape", "tabindex", "target", "title", "type", "abbr", "title", "acronym", "address", "applet", "align", "alt", "archive", "code", "codebase", "height", "hspace", "vspace", "width", "area", "accesskey", "alt", "coords", "href", "hreflang", "nohref", "shape", "tabindex", "target", "type", "article", "aside", "audio", "autoplay", "controls", "loop", "muted", "preload", "src", "b", "base", "href", "target", "basefont", "color", "face", "size", "bdi", "bdo", "dir", "bgsound", "balance", "loop", "src", "volume", "big", "blink", "blockquote", "body", "alink", "background", "bgcolor", "bgproperties", "bottommargin", "leftmargin", "link", "rightmargin", "scroll", "text", "topmargin", "vlink", "br", "clear", "button", "accesskey", "autofocus", "disabled", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "name", "type", "value", "canvas", "caption", "align", "valign", "center", "cite", "code", "col", "align", "char", "charoff", "span", "valign", "width", "colgroup", "align", "char", "charoff", "span", "valign", "width", "command", "comment", "datalist", "dd", "del", "cite", "datetime", "details", "dfn", "dir", "div", "align", "title", "dl", "dt", "em", "embed", "align", "height", "hidden", "hspace", "pluginspage", "src", "type", "vspace", "width", "fieldset", "disabled", "form", "title", "figcaption", "figure", "font", "color", "face", "size", "footer", "form", "accept-charset", "action", "autocomplete", "enctype", "method", "name", "novalidate", "target", "frame", "bordercolor", "frameborder", "name", "noresize", "scrolling", "src", "frameset", "border", "bordercolor", "cols", "frameborder", "framespacing", "rows", "h1", "align", "h2", "align", "h3", "align", "h4", "align", "h5", "align", "h6", "align", "head", "profile", "header", "hgroup", "hr", "align", "color", "noshade", "size", "width", "html", "manifest", "title", "xmlns", "i", "iframe", "align", "allowtransparency", "frameborder", "height", "hspace", "marginheight", "marginwidth", "name", "sandbox", "scrolling", "seamless", "src", "srcdoc", "vspace", "width", "img", "align", "alt", "border", "height", "hspace", "ismap", "longdesc", "lowsrc", "src", "usemap", "vspace", "width", "input", "accept", "accesskey", "align", "alt", "autocomplete", "autofocus", "border", "checked", "disabled", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "list", "max", "maxlength", "min", "multiple", "name", "pattern", "placeholder", "readonly", "required", "size", "src", "step", "tabindex", "type", "value", "ins", "cite", "datetime", "isindex", "kbd", "keygen", "label", "accesskey", "for", "legend", "accesskey", "align", "title", "li", "type", "value", "link", "charset", "href", "media", "rel", "sizes", "type", "listing", "main", "map", "name", "mark", "marquee", "behavior", "bgcolor", "direction", "height", "hspace", "loop", "scrollamount", "scrolldelay", "truespeed", "vspace", "width", "menu", "label", "type", "meta", "charset", "content", "http-equiv", "name", "meter", "high", "low", "max", "min", "optimum", "value", "multicol", "nav", "nobr", "noembed", "noframes", "noscript", "object", "align", "archive", "classid", "code", "codebase", "codetype", "data", "height", "hspace", "tabindex", "type", "vspace", "width", "ol", "reversed", "start", "type", "optgroup", "disabled", "label", "option", "disabled", "label", "selected", "value", "output", "p", "align", "param", "name", "type", "value", "valuetype", "plaintext", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "async", "defer", "language", "src", "type", "section", "select", "accesskey", "autofocus", "disabled", "form", "multiple", "name", "required", "size", "tabindex", "small", "source", "media", "src", "type", "spacer", "span", "strike", "strong", "style", "media", "type", "sub", "summary", "sup", "table", "align", "background", "bgcolor", "border", "bordercolor", "cellpadding", "cellspacing", "cols", "frame", "height", "rules", "summary", "width", "tbody", "align", "bgcolor", "char", "charoff", "valign", "td", "abbr", "align", "axis", "background", "bgcolor", "bordercolor", "char", "charoff", "colspan", "headers", "height", "nowrap", "rowspan", "scope", "valign", "width", "textarea", "accesskey", "autofocus", "cols", "disabled", "form", "maxlength", "name", "placeholder", "readonly", "required", "rows", "tabindex", "wrap", "tfoot", "align", "bgcolor", "char", "charoff", "valign", "th", "abbr", "align", "axis", "background", "bgcolor", "bordercolor", "char", "charoff", "colspan", "headers", "height", "nowrap", "rowspan", "scope", "valign", "width", "thead", "align", "bgcolor", "char", "charoff", "valign", "time", "datetime", "pubdate", "title", "tr", "align", "bgcolor", "bordercolor", "char", "charoff", "valign", "track", "tt", "u", "ul", "type", "var", "video", "autoplay", "controls", "height", "loop", "poster", "preload", "src", "width", "wbr", "xmp" }; public static bool IsStopWord(string word)