RGB画像をグレーに

この投稿では、RGB バッファをグレー (グレースケール) に変換するアルゴリズムについて説明します。
これは非常に簡単に行われ、バッファの各ピクセル カラー チャネルが特定の式に従って変換され、出力はグレーの画像になります。
平均的な方法:

red = average;
green = average;
blue = average;

Складываем 3 цветовых канала и делим на 3.

Однако существует еще один метод – метод средневзвешенный, он учитывает цветовосприятие человека:

red = luminance;
green = luminance;
blue = luminance;

Какой метод лучше использовать? Да какой вам больше подходит для конкретной задачи. Далее сравнение методов с помощью тестовой цветовой сетки:

Пример реализации на JavaScript + HTML 5

    image,
    canvas,
    weightedAverage
) {
    const context = canvas.getContext('2d');

    const imageWeight = image.width;
    const imageHeight = image.height;

    canvas.width = imageWeight;
    canvas.height = imageHeight;

    context.drawImage(image, 0, 0);

    let pixels = context
        .getImageData(
            0,
            0,
            imageWeight,
            imageHeight
        );

    for (let y = 0; y & lt; pixels.height; y++) {
        for (let x = 0; x & lt; pixels.width; x++) {
            const i = (y * 4) * pixels.width + x * 4;

            let red = pixels.data[i];
            let green = pixels.data[i + 1];
            let blue = pixels.data[i + 2]

            const average = (red + green + blue) / 3;
            const luminance = 0.2126 * red +
                0.7152 * green +
                0.0722 * blue;

            red = weightedAverage ? luminance : average;
            green = weightedAverage ? luminance : average;
            blue = weightedAverage ? luminance : average;

            pixels.data[i] = red;
            pixels.data[i + 1] = green;
            pixels.data[i + 2] = blue;
        }
    }
    context
        .putImageData(
            pixels,
            0,
            0,
            0,
            0,
            pixels.width,
            pixels.height
        );
}

Источники

https://www.baeldung.com/cs/convert-rgb-to-grayscale
https://twitter.com/mudasobwa/status/1528046455587495940
https://rosettacode.org/wiki/Grayscale_image

Ссылки

http://papugi.demensdeum.repl.co/

Благодарности

Спасибо Aleksei Matiushkin (https://twitter.com/mudasobwa) за наводку на Rosetta Code

Macbook M1 で CSGO を実行する方法

Macbook M1 で CSGO の起動時に SDL_GetDesktopDisplayMode_REAL エラーが発生した場合は、以下の手順を実行してください。
1. CSGO の起動オプションを Steam に追加します。
<コード>-w 1440 -h 900 -全画面
2.Steam経由でCSGOを起動
3. エラー SDL_GetDesktopDisplayMode_REAL
を [無視] または [常に無視] をクリックします。4. 楽しんでください

チューリング コンピューティング マシン

私はあなたの注意を引くために、アラン・チューリングの記事「–」の最初のページの翻訳を提示します。 「解像度問題への適用による計算可能な数値について」 1936年。最初の章には、後に現代のコンピューティングの基礎となるコンピューターについての説明が含まれています。

この記事と説明の完全な翻訳は、アメリカの普及者チャールズ ペッツォルトによる「チューリングを読む」というタイトルの本で読むことができます。計算可能性とチューリング マシンに関するチューリングの画期的な論文を巡る旅 -#8221; (ISBN 978-5-97060-231-7、978-0-470-22905-7)

元の記事:
https://www.astro.puc.cl/~rparra/tools/PAPERS/turing_1936.pdf

解像度問題への適用による計算可能な数値について

A. M. チューリング

[1936 年 5 月 28 日に受領 – 1936 年 11 月 12 日に読了]

計算可能な数値は、小数としての表現が有限の方法で計算できる実数として簡単に説明できます。一見すると、この記事では数値を計算可能なものとして扱いますが、整変数、実数変数、計算可能な変数、計算可能な述語などの計算可能な関数を定義して調査することは、ほぼ同じくらい簡単です。ただし、これらの計算可能なオブジェクトに関連する基本的な問題は、どの場合でも同じです。詳細な検討のために、考慮する方法が最も面倒ではないため、計算可能なオブジェクトとして計算可能な数値を選択しました。計算可能な数値と計算可能な関数などの関係については、近いうちに説明したいと思っています。同時に、計算​​可能な数値で表現される実変数の関数理論の分野でも研究が行われます。私の定義によれば、実数は、小数としての表現が機械で記述できる場合に計算可能です。

段落 9 と 10 では、計算可能な数値には、当然計算可能であると考えられるすべての数値が含まれることを示すために、いくつかの議論を示します。特に、いくつかの大きなクラスの数値が計算可能であることを示します。これらには、たとえば、すべての代数的数値の実部、ベッセル関数のゼロの実部、数値 π、e などが含まれます。ただし、次の計算不可能な定義可能な数値の例でわかるように、計算可能な数値には、定義可能な数値がすべて含まれるわけではありません。

計算可能な数値のクラスは非常に大きく、多くの点で実数のクラスと似ていますが、依然として数えることが可能です。 §8 では、反対と思われる特定の議論を検討します。これらの議論の 1 つが正しく適用されると、一見するとゲーデル* の結論と似た結論が導き出されます。これらの結果は非常に重要な応用分野を持っています。特に、以下 (§11) に示すように、解像度の問題は解決できません。

アロンゾ チャーチ** は、最近の記事で「実効計算可能性」という考え方を紹介しました。これは、私の「計算可能性」という考え方と同じですが、定義はまったく異なります。チャーチも解決の問題に関して同様の結論に達しています。 「計算可能性」と「効率的に数えられる能力」が同等であることの証明は、この記事の付録に記載されています。

1.コンピュータ

計算可能な数値とは、小数点以下の桁が有限の方法で数えられる数値であることはすでに述べました。ここではより明確な定義が必要です。この記事では、§9 に到達するまで、ここで与えられた定義を正当化する実際の試みは行いません。現時点では、(このための)(論理的な)根拠は、人間の記憶には必然的に限界があるということだけを述べておきます。

実数を計算している人間と、有限数の条件 q1、q2、…、qR のみを満たすことができるマシンを比較してみましょう。これらの条件を「m 構成」と呼びます。この(つまり、そのように定義された)マシンには「テープ」(紙に似たもの)が装備されています。機械内を通過するこのベルトはいくつかのセクションに分かれています。それらを「正方形」と呼びましょう。このような各正方形には、何らかの「シンボル」を含めることができます。いつでも、「このマシン内にある」シンボルを含むそのような正方形は 1 つだけ、たとえば r 番目の正方形だけです。このような正方形を「スキャンされたシンボル」と呼びます。 「スキャンされた文字」は、いわばマシンが「直接認識している」唯一の文字です。ただし、m 構成を変更することで、マシンは以前に「見た」(スキャンした) 文字の一部を効果的に記憶できるようになります。いつでもマシンの可能な動作は、m 構成 qn とスキャンされたシンボル *** によって決まります。このシンボルのペアを qn、「構成」と呼びましょう。このように指定された構成によって、特定のマシンの可能な動作が決まります。スキャンされた正方形が空白である (つまり、文字が含まれていない) 構成の一部では、マシンはスキャンされた正方形に新しい文字を書き込み、その他の構成ではスキャンされた文字を消去します。このマシンは移動して別のマスをスキャンすることもできますが、この方法では右または左に隣接するマスにのみ移動できます。これらの操作に加えて、マシンの m 構成を変更することもできます。この場合、書かれた文字の一部は、計算される実数の小数部分である一連の数字を形成します。残りは「記憶を助ける」ための不正確なマークにすぎません。この場合、上記の不正確なマークのみを消去できます。

ここで考慮される操作には、計算で使用されるすべての操作が含まれると主張します。このステートメントの理論的根拠は、機械理論を理解している読者にとっては理解しやすいものです。したがって、次のセクションでは、「機械」、「テープ」、「スキャンされた」などの用語の意味の理解に基づいて、問題の理論を展開していきます

*ゲーデル「プリンキピア数学 (1910 年、1912 年、1913 年にホワイトヘッドとラッセルによって出版) および関連システムの形式的に決定不可能な文について、第 1 部」Journal of Mathematics。物理学、ドイツ語月刊誌第 38 号 (1931 年、173-198 ページ。
** アロンゾ チャーチ、「初等整数理論における決定不可能な問題」、American J. of Math.、第 58 号 (1936 年)、345-363 ページ。
*** アロンゾ チャーチ、「解決の問題に関するメモ」、J. of Symbolic Logic、No. 1 (1936)、40-41 ページ

チューリング爆弾

1936 年、科学者のアラン チューリングは、著書『計算可能な数について、現実の問題への応用』の中で、数学における可解性の問題に終止符を打つ可能性のある万能計算機の使用について説明しました。その結果、彼は、そのようなマシンの作業結果が反転され、それ自体でループバックされる場合、そのようなマシンは何も正しく解決できないだろうという結論に達しました。 *理想的な* アンチウイルス、*理想的な* タイル セッター、クラッシュに対して理想的なフレーズを提案するプログラムなどを作成するのは不可能であることが判明しました。パラドックス!

しかし、この汎用コンピューティング マシンはあらゆるアルゴリズムの実装に使用でき、英国諜報機関はそれを利用してチューリングを雇い、第二次世界大戦中にドイツのメッセージを解読するための「ボンベ」マシンの作成を許可しました。

以下は、元のドキュメントに基づいた、Dart 言語による単一テープ コンピューターの OOP モデリングです。

チューリング マシンはセクションに分割されたフィルムで構成されており、各セクションにはシンボルが含まれており、シンボルは読み書きできます。映画クラスの例:

final _map = Map<int, String>(); 

  String read({required int at}) { 
    return _map[at] ?? ""; 
  } 

  void write({required String symbol, required int at}) { 
    _map[at] = symbol; 
  } 
}

「スキャン スクエア」もあり、フィルム上を移動して情報を読み書きすることができます (現代語で言えば –)。磁気ヘッド。磁気ヘッド クラスの例:

  int _index = 0; 
  InfiniteTape _infiniteTape; 
  TapeHead(this._infiniteTape) {} 

  String next() { 
    _index += 1; 
    move(to: _index); 
    final output = read(); 
    return output; 
  } 

  String previous() { 
    _index -= 1; 
    move(to: _index); 
    final output = read(); 
    return output; 
  } 

  void move({required int to}) { 
    this._index = to; 
  } 

  String read() { 
    return _infiniteTape.read(at: this._index); 
  } 

  void write(String symbol) { 
    _infiniteTape.write(symbol: symbol, at: this._index); 
  } 

  int index() { 
    return _index; 
  } 
} 

マシンには、次に何を行うかを決定できる「m 構成」が含まれています。現代語で–状態と状態ハンドラー。状態ハンドラーの例:

  FiniteStateControlDelegate? delegate = null; 

  void handle({required String symbol}) { 
    if (symbol == OPCODE_PRINT) { 
      final argument = delegate?.nextSymbol(); 
      print(argument);
    } 
    else if (symbol == OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER) { 
      final to = int.tryParse(delegate!.nextSymbol())!; 
      final value = new Random().nextInt(to); 
      delegate!.nextSymbol(); 
      delegate!.write(value.toString()); 
    } 
    else if (symbol == OPCODE_INPUT_TO_NEXT) { 
      final input = stdin.readLineSync()!; 
      delegate?.nextSymbol(); 
      delegate?.write(input); 
    } 
    else if (symbol == OPCODE_COPY_FROM_TO) { 
      final currentIndex = delegate!.index(); 

и т.д. 

この後、「構成」を作成する必要があります。現代の言葉で言うと、これらはオペレーション コード (オペコード) とそのハンドラーです。オペコードの例:

const OPCODE_PRINT = "print"; 
const OPCODE_INCREMENT_NEXT = "increment next"; 
const OPCODE_DECREMENT_NEXT = "decrement next"; 
const OPCODE_IF_PREVIOUS_NOT_EQUAL = "if previous not equal"; 
const OPCODE_MOVE_TO_INDEX = "move to index"; 
const OPCODE_COPY_FROM_TO = "copy from index to index"; 
const OPCODE_INPUT_TO_NEXT = "input to next"; 
const OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER = "generate random number from zero to next and write after"; 

オペコードと停止ハンドラーを作成することを忘れないでください。そうしないと、解決の問題を証明できないか、証明できないことになります。

次に、「メディエーター」パターンを使用して、チューリング マシン クラス内のすべてのクラスを接続し、クラスのインスタンスを作成し、テープ レコーダーでプログラムを録音し、テープをロードすると、使用できるようになります。

私個人にとって、何が主要であるかという問題は依然として興味深いものでした –万能計算機の作成、または「Entscheidungsproblem」の証明、その結果副産物として計算機が登場しました。

カセット

楽しみのために、自分のバージョンのマシン用にいくつかのカセット プログラムを録音しました。

ハローワールド

hello world 
stop

Считаем до 16-ти

0
if previous not equal
16
copy from index to index
1
8
print
?
move to index
0
else
copy from index to index
1
16
print
?
print
Finished!
stop

Самой интересной задачей было написание Quine программы, которая печатает свой исходный код, для одноленточной машины. Первые 8 часов мне казалось что эта задача не решаема с таким малым количеством опкодов, однако всего через 16 часов оказалось что я был не прав.

Реализация и примеры кассет, источники ниже.

Ссылки

https://gitlab.com/demensdeum/turing-machine

Источники

https://www.astro.puc.cl/~rparra/tools/PAPERS/turing_1936.pdf
https://kpolyakov.spb.ru/prog/turing.htm
https://www.youtube.com/watch?v=dNRDvLACg5Q
https://www.youtube.com/watch?v=jP3ceURvIYc
https://www.youtube.com/watch?v=9QCJj5QzETI
https://www.youtube.com/watch?v=HeQX2HjkcNo&t=0s

Sega Genesis #5 のアセンブリでの書き込み

このメモでは、ジョイスティックの読み取り、スプライトの位置の変更、水平方向の反転、Sega Genesis エミュレータ、および場合によってはコンソール自体のプロセスについて説明します。

将棋ジョイスティックのクリックの読み取りと「イベント」の処理は、次のスキームに従って行われます。

<オル>

  • 押されたボタンのビットの組み合わせをリクエストする
  • 押されたボタンのビットを読み取る
  • ゲームロジックレベルでの処理
  • スケルトン スプライトを移動するには、現在の位置の変数を保存する必要があります。

    RAM

    ゲーム ロジック変数は RAM に保存されます。これより優れたものはまだ見つかっていません。変数アドレスを宣言して、レンダリング コードを変更しましょう。

    skeletonYpos = $FF0002 
    frameCounter = $FF0004 
    skeletonHorizontalFlip = $FF0006
    
        move.w #$0100,skeletonXpos 
        move.w #$0100,skeletonYpos 
        move.w #$0001,skeletonHorizontalFlip 
    
    FillSpriteTable: 
        move.l #$70000003,vdp_control_port 
        move.w skeletonYpos,vdp_data_port  
        move.w #$0F00,vdp_data_port 
        move.w skeletonHorizontalFlip,vdp_data_port 
        move.w skeletonXpos,vdp_data_port 
    

    ご覧のとおり、作業に利用できるアドレスは 0xFF0000 で始まり 0xFFFFFF で終わり、合計 64 KB のメモリが利用可能です。スケルトンの位置は、skeletonXpos、skeletonYpos で宣言され、水平方向の反転は、skeletonhorizo​​ntalflip で宣言されます。

    ジョイパッド

    VDP と同様に、ジョイパッドの操作は 2 つのポートを介して個別に行われます。制御ポートとデータ ポート。最初のポートは 0xA10009 と 0xA10003 の共通番号です。ジョイパッドを使用する場合、興味深い機能が 1 つあります。まず、ポーリング用のボタンの組み合わせをリクエストする必要があります。次に、バス上の更新を待った後、必要なボタンの押し方を読み取ります。 C/B および D パッド ボタンの場合、これは 0x40 です。以下の例:

      move.b #$40,joypad_one_control_port; C/B/Dpad 
      nop ; bus sync 
      nop ; bus sync 
      move.b joypad_one_data_port,d2 
      rts 
    

    レジスタ d2 では、ボタンが押されたか押されなかったかの状態が残ります。一般に、日付ポートを通じて要求された内容が残ります。その後、お気に入りのエミュレータの Motorola 68000 レジスタ ビューアに移動し、キーストロークに応じて d2 レジスタが何に等しいかを確認します。賢明な方法では、マニュアルでそれを知ることができますが、私たちは彼らの言葉を鵜呑みにしません。 d2

    レジスタで押されたボタンのさらなる処理

        cmp #$FFFFFF7B,d2; handle left 
        beq MoveLeft  
        cmp #$FFFFFF77,d2; handle right  
        beq MoveRight  
        cmp #$FFFFFF7E,d2; handle up  
        beq MoveUp  
        cmp #$FFFFFF7D,d2; handle down  
        beq MoveDown  
        rts

    Проверять нужно конечно отдельные биты, а не целыми словами, но пока и так сойдет. Теперь осталось самое простое – написать обработчики всех событий перемещения по 4-м направлениям. Для этого меняем переменные в RAM, и запускаем процедуру перерисовки.

    Пример для перемещения влево + изменение горизонтального флипа:

        move.w skeletonXpos,d0 
        sub.w #1,d0 
        move.w d0,skeletonXpos 
        move.w #$0801,skeletonHorizontalFlip 
        jmp FillSpriteTable

    После добавления всех обработчиков и сборки, вы увидите как скелет перемещается и поворачивается по экрану, но слишком быстро, быстрее самого ежа Соника.

    Не так быстро!

    Чтобы замедлить скорость игрового цикла, существуют несколько техник, я выбрал самую простую и не затрагивающую работу с внешними портами – подсчет цифры через регистр пока она не станет равна нулю.

    Пример замедляющего цикла и игрового цикла:

      move.w #512,frameCounter 
    WaitFrame: 
      move.w frameCounter,d0 
      sub.w #1,d0 
      move.w d0,frameCounter 
      dbra d0,WaitFrame 
    GameLoop: 
      jsr ReadJoypad 
      jsr HandleJoypad 
      jmp GameLoop 
    

    この後、スケルトンの実行速度が遅くなりますが、これは必要なことです。私が知っているように、「速度を落とす」ための最も一般的なオプションは、垂直同期フラグをカウントすることです。これにより、画面が描画された回数をカウントできるため、特定の fps に関連付けられます。

    リンク

    https://gitlab .com/demensdeum/segagenesisamples/-/blob/main/8Joypad/vasm/main.asm

    ソース

    https://www.chibiakumas.com/68000/platform2.php
    https://huguesjohnson.com/programming/genesis/tiles-sprites/