シェルスクリプトマガジン

test

香川大学SLPからお届け!(Vol.92掲載)

投稿日:2024.09.25 | カテゴリー: コード

著者:池内稜來斗

各都道府県の都道府県庁所在地を尋ねるクイズを出題するWebサイトをPHPで作成しました。今回は、そのWebサイトの概要や作成手順などを紹介します。PHPの実行環境や、各種サーバーソフトウエアは、「XAMPP」(https://www.apachefriends.org/)を使ってインストールします。

シェルスクリプトマガジン Vol.92は以下のリンク先でご購入できます。

図2 「db.sql」ファイルに記述するSQL文

use pref_capitals_quiz;
insert into pref_capitals
  (id, name_p, name_c, image_p) values
(1, '北海道', '札幌市', 'img/hokkaidou.png'),
(2, '青森県', '青森市', 'img/aomori.png'),
(3, '岩手県', '盛岡市', 'img/iwate.png'),
(4, '秋田県', '秋田市', 'img/akita.png'),
(5, '宮城県', '仙台市', 'img/miyagi.png'),
(6, '山形県', '山形市', 'img/yamagata.png'),
(7, '福島県', '福島市', 'img/fukushima.png'),
(8, '茨城県', '水戸市', 'img/ibaraki.png'),
(9, '栃木県', '宇都宮市', 'img/tochigi.png'),
(10, '群馬県', '前橋市', 'img/gunma.png'),
(11, '埼玉県', 'さいたま市', 'img/saitama.png'),
(12, '千葉県', '千葉市', 'img/chiba.png'),
(13, '東京都', '東京', 'img/tokyo.png'),
(略)
(47, '沖縄県', '那覇市', 'img/okinawa.png');
commit;

図3 「index.html」ファイルに記述するコード

<html lang=ja>
<head>
  <meta charset="utf-8">
  <title>都道府県庁所在地クイズ!!</title>
</head>
<body>
  <h1>都道府県庁所在地クイズ!!</h1>
  <p>表示された都道府県の都道府県庁所在地を回答してください。<br>
  東京都の場合は、区名と地域名のどちらで回答しても構いません。<br>
  すべての都道府県について回答したら終了です。
  </p>
  <button type="button"
          onclick="location.href='question.php'">
  クイズを始める
  </button>
</body>
</html>

図4 「question.php」ファイルに記述するコード

<?php
  session_start();
  if (!isset($_SESSION['times'])) {
    $_SESSION['times'] = 0;
    $_SESSION['right'] = 0;
  }
  if ($_SESSION['times'] == 0) {
    $r = new \Random\Randomizer();
    $order = $r->shuffleArray(range(1, 47));
    $_SESSION['order'] = $order;
  }
  $order = $_SESSION['order'];
  $times = $_SESSION['times'];
  $pref = $order[$times];
?>

<html lang=ja>
<head>
  <meta charset="UTF-8">
  <title>都道府県庁所在地クイズ!!</title>
</head>
<body>

<?php
  $link = mysqli_connect('localhost','root','',
                         'pref_capitals_quiz');
  mysqli_set_charset($link,'utf8');
  $sql = "SELECT id, name_p, name_c, image_p " .
         "FROM pref_capitals WHERE id = $pref";
  $result = mysqli_query($link, $sql);
  $row = mysqli_fetch_assoc($result);
  $path = $row['image_p'];
  echo "<br>";
  echo "問題", $times + 1;
  echo '<img src="' . $path .
       '" alt="' . $row['name_p'] . 'の地図">';
  print('  '.$row['name_p']);
?>

<form method="POST" class="form" action="answer.php">
  <input type="text" name="Answer" class="input">
  <p>
    <input type="submit" value="回答" class="submit">
  </p>
</form>
</body>
</html>

図5 「answer.php」ファイルに記述するコード

<?php
  session_start();
  $times = $_SESSION['times'];
  $order = $_SESSION['order'];
  $right = $_SESSION['right'];
  $pref = $order[$times];
?>

<html lang=ja>
<head>
  <meta charset="UTF-8">
  <title>都道府県庁所在地クイズ!!</title>
</head>
<body>

<?php
  $received_Answer = $_POST['Answer'];
  $link = mysqli_connect('localhost','root','',
                         'pref_capitals_quiz');
  mysqli_set_charset($link,'utf8');
  $sql = "SELECT id, name_p, name_c, image_p " .
         "FROM pref_capitals WHERE id = $pref";
  $result = mysqli_query($link, $sql);
  $row = mysqli_fetch_assoc($result);
  // 東京都かどうか
  if ($row['id'] == 13) {
    if (($received_Answer == $row['name_c']) ||
      ($received_Answer == '新宿区')) {
      echo "正解\n";
      $right++;
    }
  } else if (($received_Answer == $row['name_c']) ||
    (($received_Answer . "市") == $row['name_c'])) {
    echo "正解\n";
    $right++;
  } else {
    echo "不正解\n";
  }
  $_SESSION['times'] = ++$times;
  $_SESSION['right'] = $right;
  echo "<br>";
  echo "問題{$times} {$row['name_p']} の答え";
  echo "<br>";
  echo $row['name_c'];

  if ($times != 47) {
    echo '
    <form method="POST" class="form" action="question.php">
      <button type="submit">次の問題</button>
    </form>';
  }
?>

  <form method="POST" class="form" action="result.php">
    <button type="submit">回答終了</button>
  </form>
</body>
</html>

図6 「result.php」ファイルに記述するコード

<?php
  session_start();
  if (isset($_POST['reset'])) {
    $_SESSION['times'] = 0;
    $_SESSION['right'] = 0;
    header("Location: index.html");
  }
?>

<html lang=ja>
<head>
  <meta charset="UTF-8">
  <title>都道府県庁所在地クイズ!!</title>
</head>
<body>

<?php
  echo "今回の正答数は<br>";
  echo $_SESSION['times'], "問中",
       $_SESSION['right'], "問";
  echo "<br>";
  echo "お疲れさまでした<br>";
?>

  <form method="POST" action="result.php">
    <input type="submit"
           value="トップページに戻る"
           name="reset">
  </form>
</body>
</html>

Markdownを活用する(Vol.92掲載)

投稿日:2024.09.25 | カテゴリー: コード

著者:藤原 由来

本連載では文書の装飾・構造付けを手軽に行える記法であるMarkdownを用いて、さまざまな文書や成果物を作成する方法を紹介します。今回は前回に引き続き、Markdownを用いて書籍を制作できる組版アプリ「Vivliostyle」について解説します。特に、技術同人誌の制作方法と、印刷所へ入稿する際の注意
点について説明します。 

シェルスクリプトマガジン Vol.92は以下のリンク先でご購入できます。

図7 書き換えたVivliostyleの設定ファイル

module.exports = {
  title: 'sample-book-kagakunofushigi', 
  author: 'アンリイ・ファブル(大杉栄、伊藤野枝訳)', 
  language: 'ja',
  size: 'JIS-B5',
  theme: '@vivliostyle/theme-techbook@^1.0.1', 
  entryContext: './manuscripts',
  entry: [
   'index.md',
   '01.md',
   '02.md',
   '03.md',
   '04.md',
   '05.md',
   '06.md',
   '07.md',
   '08.md',
   '09.md',
   'colophon.md',
  ],
  workspaceDir: '.vivliostyle',
}

図8 扉のindex.mdファイル

# 科学の不思議
アンリイ・ファブル(大杉栄、伊藤野枝訳)

© 某サークル, 20xx

図9 奥付のcolophon.mdファイル

## 科学の不思議

20xx年x月x日 初版発行

----------------

- 発行 某サークル
- 著者 アンリイ・ファブル
- 翻訳 大杉栄、伊藤野枝
- 編集 シェル花子
- 連絡先 foo@example.com
- 印刷 サンプル印刷

----------------

底本は青空文庫から転載・改変しました。

- ファーブル ジャン・アンリ『科学の不思議』(大杉 栄、伊藤 野枝 訳)

© 某サークル, 20xx

図11 my-theme.cssファイル内のCSSを反映する設定

  theme: [
    '@vivliostyle/theme-techbook@^1.0.1',
    'themes/my-theme.css',
  ],

図12 扉と奥付用のCSS

/* 扉 */
section#title-page {
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  height: 50vh;
  margin: 0;
}

/* 奥付 */
section#colophon {
  position: relative;
  float-reference: page;
  float: bottom;
  margin-bottom: 0;
}

図13 扉のindex.mdファイルの変更

<section id="title-page">

# 科学の不思議

アンリイ・ファブル(大杉栄、伊藤野枝訳)

© 某サークル, 20xx

</section>

図14 奥付のcolophon.mdファイルの変更

<section id="colophon" role="doc-colophon">

## 科学の不思議

20xx年x月x日 初版発行

----------------

- 発行 某サークル
- 著者 アンリイ・ファブル
- 翻訳 大杉栄、伊藤野枝
- 編集 シェル花子
- 連絡先 foo@example.com
- 印刷 サンプル印刷

----------------

底本は青空文庫から転載・改変しました。

- ファーブル ジャン・アンリ『科学の不思議』(大杉 栄、伊藤 野枝 訳)

© 某サークル, 20xx

</section>

図17 目次を自動設定する設定

(略)
  entryContext: './manuscripts',
  toc: {
    htmlPath: 'toc.html', 
    title: '目次',
  },
  entry: [
   'index.md',
    { rel: 'contents' },
   '01.md',
(略)

Pythonあれこれ(Vol.92掲載)

投稿日:2024.09.25 | カテゴリー: コード

著者:飯尾 淳

本連載では「Pythonを昔から使っているものの、それほど使いこなしてはいない」という筆者が、いろいろな日常業務をPythonで処理することで、立派な「蛇使い」に育つことを目指します。その過程を温かく見守ってください。皆さんと共に勉強していきましょう。第22回では、機械学習ライブラリ「PyTorch」を使って、画像内の、歩行者が写っている領域を自動認識するPythonプログラムを作成します。

シェルスクリプトマガジン Vol.92は以下のリンク先でご購入できます。

図3 47番のデータを確認するPythonコード

import matplotlib.pyplot as plt
from torchvision.io import read_image

image =read_image(
    "data/PennFudanPed/PNGImages/FudanPed00047.png")
mask = read_image(
    "data/PennFudanPed/PedMasks/FudanPed00047_mask.png")
plt.figure(figsize=(16, 8))
plt.subplot(121)
plt.title("Image")
plt.imshow(image.permute(1, 2, 0))
plt.subplot(122)
plt.title("Mask")
plt.imshow(mask.permute(1, 2, 0))

図5 PennFudanDataSetクラスを定義するPythonコード

import os
import torch
from torchvision.io import read_image
from torchvision.ops.boxes import masks_to_boxes
from torchvision import tv_tensors
from torchvision.transforms.v2 import functional as F

class PennFudanDataset(torch.utils.data.Dataset):
  def __init__(self, root, transforms):
    self.root = root
    self.transforms = transforms
    # イメージデータとマスクデータをロードし、ソートして並べておく
    self.imgs = \
      list(sorted(os.listdir(os.path.join(root, "PNGImages"))))
    self.masks = \
      list(sorted(os.listdir(os.path.join(root, "PedMasks"))))

  def __getitem__(self, idx):
    # イメージとマスクのロード
    img_path = \
      os.path.join(self.root, "PNGImages", self.imgs[idx])
    mask_path = \
      os.path.join(self.root, "PedMasks", self.masks[idx])
    img = read_image(img_path)
    mask = read_image(mask_path)
    # インスタンスは色別にエンコードされていて……
    obj_ids = torch.unique(mask)
    # 最初のIDは背景色なので削除
    obj_ids = obj_ids[1:]
    num_objs = len(obj_ids)
    # 色別にエンコードされているマスクを2値マスクに分ける
    masks = \
      (mask == obj_ids[:, None, None]).to(dtype=torch.uint8)
    # マスクデータに対してバウンディングボックスを求める
    boxes = masks_to_boxes(masks)
    labels = torch.ones((num_objs,), dtype=torch.int64)
    image_id = idx
    area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])
    # 群衆でないと仮定
    iscrowd = torch.zeros((num_objs,), dtype=torch.int64)
    # tv_tensorsイメージに変換する
    img = tv_tensors.Image(img)
    target = {}
    target["boxes"] = \
      tv_tensors.BoundingBoxes(boxes,
        format="XYXY", canvas_size=F.get_size(img))
    target["masks"] = tv_tensors.Mask(masks)
    target["labels"] = labels
    target["image_id"] = image_id
    target["area"] = area
    target["iscrowd"] = iscrowd

    if self.transforms is not None:
      img, target = self.transforms(img, target)

    return img, target

  def __len__(self):
    return len(self.imgs)

図6 get_model_instance_segmentation()関数を定義するPythonコード

import torchvision
from torchvision.models.detection.faster_rcnn \
  import FastRCNNPredictor
from torchvision.models.detection.mask_rcnn \
  import MaskRCNNPredictor

def get_model_instance_segmentation(num_classes):
  # COCOで事前学習済みのモデルデータをロードする
  model = torchvision.models.detection.maskrcnn_resnet50_fpn(
            weights="DEFAULT")
  # 入力特徴量
  in_features = \
    model.roi_heads.box_predictor.cls_score.in_features
  # num_classesで指定するクラスの分類器をセット(今回は2クラス)
  model.roi_heads.box_predictor = \
    FastRCNNPredictor(in_features, num_classes)
  # マスク判別器も同様に設定
  in_features_mask = \
    model.roi_heads.mask_predictor.conv5_mask.in_channels
  hidden_layer = 256
  model.roi_heads.mask_predictor = MaskRCNNPredictor(
    in_features_mask,
    hidden_layer,
    num_classes
  )
  return model

図7 get_transform()関数を定義するPythonコード

from torchvision.transforms import v2 as T

def get_transform(train):
  transforms = []
  if train:
    transforms.append(T.RandomHorizontalFlip(0.5))
  transforms.append(T.ToDtype(torch.float, scale=True))
  transforms.append(T.ToPureTensor())
  return T.Compose(transforms)

図8 追加学習の準備をするためのPythonコード

import utils
from engine import train_one_epoch, evaluate

device = torch.device('cuda') \
  if torch.cuda.is_available() else torch.device('cpu')
# 背景と人物の2クラス判別器を作成する
num_classes = 2
# データセットはすでにロード済みのものを用いる
dataset = \
  PennFudanDataset('data/PennFudanPed',
                   get_transform(train=True))
dataset_test = \
  PennFudanDataset('data/PennFudanPed',
                   get_transform(train=False))
# データセットを学習用とテスト用に分ける
indices = torch.randperm(len(dataset)).tolist()
dataset = torch.utils.data.Subset(dataset, indices[:-50])
dataset_test = torch.utils.data.Subset(
                 dataset_test, indices[-50:])
# 学習と評価用のデータローダを定義
data_loader = torch.utils.data.DataLoader(
  dataset,
  batch_size=2,
  shuffle=True,
  num_workers=4,
  collate_fn=utils.collate_fn
)
data_loader_test = torch.utils.data.DataLoader(
  dataset_test,
  batch_size=1,
  shuffle=False,
  num_workers=4,
  collate_fn=utils.collate_fn
)
# 先ほど定義したヘルパーファンクションを用いてモデルを用意
model = get_model_instance_segmentation(num_classes)
# モデルをデバイスに結び付ける
model.to(device)
# 最適化器を作成
params = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.SGD(
  params,
  lr=0.005,
  momentum=0.9,
  weight_decay=0.0005
)
# 学習レートのスケジューラを設定
lr_scheduler = torch.optim.lr_scheduler.StepLR(
  optimizer,
  step_size=3,
  gamma=0.1
)

図9 物体認識と領域セグメンテーションを実施するPythonコード

import matplotlib.pyplot as plt
from torchvision.utils \
  import draw_bounding_boxes, draw_segmentation_masks

image = \
  read_image("data/PennFudanPed/PNGImages/FudanPed00047.png")
eval_transform = get_transform(train=False)
model.eval()
with torch.no_grad():
  x = eval_transform(image)
  # RGBA -> RGBにコンバートしてデバイスにひも付ける
  x = x[:3, ...].to(device)
  predictions = model([x, ])
  pred = predictions[0]
image = (255.0 * (image - image.min()) / 
        (image.max() - image.min())).to(torch.uint8)
image = image[:3, ...]
pred_labels = [f"pedestrian: {score:.3f}" for label, \
               score in zip(pred["labels"], pred["scores"])]
pred_boxes = pred["boxes"].long()
output_image = draw_bounding_boxes(image, 
                 pred_boxes, pred_labels, colors="red")
masks = (pred["masks"] > 0.7).squeeze(1)
output_image = draw_segmentation_masks(output_image, 
                 masks, alpha=0.5, colors="blue")
plt.figure(figsize=(12, 12))
plt.imshow(output_image.permute(1, 2, 0))

図11 Penn-Fudanデータセットを用いて追加学習するPythonコード

# 2エポックだけ学習させてみる
num_epochs = 2
for epoch in range(num_epochs):
  # 10回ごとに表示させながら1エポックの学習を実行
  train_one_epoch(model, optimizer, data_loader, 
                  device, epoch, print_freq=10)
  # 学習レートをアップデート
  lr_scheduler.step()
  # テストデータで評価
  evaluate(model, data_loader_test, device=device)

特別企画 イノシシ撃退機を作る ソフト作成編(Vol.92記載)

投稿日:2024.09.25 | カテゴリー: コード

著者:米田 聡

「Interface」「CQ ham radio」「トランジスタ技術」などの雑誌を出版するCQ出版社の電子部品セット「イノシシ撃退機部品セットVer.1」を組み立て、ソフトウエアに一工夫を加えてイノシシ撃退機を作ります。今回は完成したハードウエア向けにソフトウエアを作成します。

シェルスクリプトマガジン Vol.92は以下のリンク先でご購入できます。

図18 PWM機能の初期化

(略)
#define PWM_PIN 20
(略)
#define PWM_SLICE_NUM pwm_gpio_to_slice_num(PWM_PIN)
(略)
void pwm_gpio_init() {
(略)
    gpio_set_function(PWM_PIN, GPIO_FUNC_PWM);
    pwm_set_wrap(PWM_SLICE_NUM, 4095);
    pwm_set_clkdiv(PWM_SLICE_NUM, 2);
    pwm_set_irq_enabled(PWM_SLICE_NUM, true);

    irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
    irq_set_enabled(PWM_IRQ_WRAP, true);
(略)
}
(略)

図20 割り込みハンドラ

void on_pwm_wrap() {
    pwm_clear_irq(PWM_SLICE_NUM);
(略)
    uint16_t duty = convert_flash_data_to_duty(spi_rx_buf[flash_data_index], spi_rx_buf[flash_data_index + 1]);
    pwm_set_chan_level(PWM_SLICE_NUM, PWM_CHAN_A, duty);
    flash_data_index += 2;
    if (flash_data_index >= 256) {
        flash_data_index = 0;
    }
(略)
}

図21 ダブルバッファを使用する修正

■34~37行目の修正コード
#ifdef FLASH_DATA_TO_DUTY
// ダブルバッファ化
uint8_t spi_rx_buf[2][256];
volatile uint32_t flash_data_index = 0;
#endif

■45~54行目の修正コード
// バッファ選択
volatile uint32_t buf_flip = 0;

void on_pwm_wrap() {
    pwm_clear_irq(PWM_SLICE_NUM);

    #ifdef FLASH_DATA_TO_DUTY
    uint16_t duty = convert_flash_data_to_duty(spi_rx_buf[buf_flip][flash_data_index], spi_rx_buf[buf_flip][flash_data_index + 1]);
    pwm_set_chan_level(PWM_SLICE_NUM, PWM_CHAN_A, duty);
    flash_data_index += 2;
    if (flash_data_index >= 256) {
        // バッファ切り替え
        buf_flip ^= 1;
        flash_data_index = 0;
    }
}

■87~105行目の修正コード
void play_sound() {
    // バッファ切り替え(ローカル)
    int flip = 0;

    flash_data_index = 0;
    buf_flip = 0;
    pwm_set_enabled(PWM_SLICE_NUM, true);	//mu0918

    for(uint32_t address = 0; address < FLASH_SIZE; address += 256) {
        gpio_put(PICO_DEFAULT_SPI_SSEL_PIN, 0);  // Set the CS pin to active LOW
        
        // Send the read command
        uint8_t spi_tx_buf[] = {0x03, (address >> 16) & 0xFF, (address >> 8) & 0xFF, address & 0xFF};
        spi_write_blocking(spi, spi_tx_buf, 4);

        // Read the data
        spi_read_blocking(spi, 0, spi_rx_buf[flip], 256);
        // バッファ切り替え
        flip ^= 1;
        
        gpio_put(PICO_DEFAULT_SPI_SSEL_PIN, 1);  // Set the CS pin to non-active HIGH
    }
    pwm_set_enabled(PWM_SLICE_NUM, false);	//mu0918
}

シェルスクリプトマガジンvol.92 Web掲載記事まとめ

投稿日:2024.09.25 | カテゴリー: コード

004 レポート 仮想化ソフト「VirtualBox」の新版リリース
005 レポート マイコンボード「Raspberry Pi Pico 2」登場
006 製品レビュー 組み込み機器「CH32V003ボード」
007 NEWS FLASH
008 特集1 古いRaspberry Piを復活させる/麻生二郎
016 特集2 統合運用管理ソフトウエア Hinemos入門/倉田晃次、加藤達也
026 特別企画 イノシシ撃退機を作る ソフト作成編/米田聡 コード掲載
036 特別企画 Rocky Linux 9のインストール/麻生二郎
042 Pythonあれこれ/飯尾淳 コード掲載
048 Markdownを活用する/藤原由来 コード掲載
058 中小企業手作りIT化奮戦記/菅雄一
062 香川大学SLPからお届け!/池内稜來斗 コード掲載
068 これだけは覚えておきたいLinuxコマンド/大津真
078 Techパズル/gori.sh
079 コラム「SEの働き方の新しい仕組みを作る」/シェル魔人 

Vol.92

投稿日:2024.09.25 | カテゴリー: バックナンバー

 小型コンピュータボード「Raspberry Pi」が登場して約12年が過ぎました。現在では、AI(人工知能)処理ができ、パソコン並みの性能を持つものに進化しています。ただ、センサーなどの制御、液晶や有機ELなどのパネルへの出力といった分野では、初期のRaspberry Piでも十分活躍できます。特集1では、初期のRaspberry Piを復活する方法を紹介しています。具体的には、無線通信機能を付加して使い勝手を向上します。
 特集2では、NTTデータ最先端技術が開発する統合運用管理ソフトウエア「Hinemos」を解説しています。同ソフトウエアは、オープンソースであり、誰でも無料で利用できます。サーバーやネットワークなどのシステムの監視やジョブ管理、運用の自動化を実現します。
い。
 特別企画では、前号に引き続きイノシシ撃退機、加えてCentOS Linuxの代替となるLinuxディストリビューション「Rocky Linux」を扱っています。
 このほか、連載「Pythonあれこれ」では機械学習ライブラリ「PyTorch」でAIによる歩行者認識の実現方法を紹介しています。
 今回も読み応え十分のシェルスクリプトマガジン Vol.92。お見逃しなく!

※記事掲載のコードはこちら。記事の補足情報はこちら

※読者アンケートはこちら

Vol.92 補足情報

投稿日:2024.09.19 | カテゴリー: コード

連載 香川大学SLPからお届け!

記事中で作成したクイズWebサイト用のコードや、データベースへのデータ入力用のファイルをまとめたアーカイブファイルがここからダウンロードできます。

情報は随時更新致します。

  • shell-mag ブログの 2024年9月 のアーカイブを表示しています。

  • -->