Spring Security 7にアップグレードしたらxmlでの設定が使えなくなった

XMLファイルでSpring Securityを設定するSecurityConfigは非推奨になっていたが、今回完全に削除されたのかクラスファイルが見つからなくなった。

流石にJavaファイルで設定する方式に変更した。

元のxmlファイルは下記の通り。
※ExtendedAuthenticationSuccessHandler、ExtendedAuthenticationFailureHandlerはログイン成功失敗時に独自処理を行うためのラッパークラス。

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">

    <!-- Resources not processed by spring security filters -->
    <http pattern="/images/**" security="none" />
    <http pattern="/scripts/**" security="none" />
    <http pattern="/styles/**" security="none" />

    <http>
        <intercept-url pattern="/error" access="permitAll" />
        <intercept-url pattern="/login*/**" access="permitAll" />
        <intercept-url pattern="/signup*" access="permitAll" />
        <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/**" access="isAuthenticated()" />

        <form-login login-page="/login" authentication-success-handler-ref="authenticationSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler" />
        <remember-me user-service-ref="userDetails" key="aaa" />
        <logout logout-url="/logout" logout-success-url="/login" invalidate-session="true" delete-cookies="aaa" />
    </http>

    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="userDetails">
            <password-encoder ref="passwordEncoder" />
        </authentication-provider>
    </authentication-manager>

    <beans:bean id="authenticationSuccessHandler" class="common.webapp.filter.ExtendedAuthenticationSuccessHandler">
        <beans:constructor-arg value="/top" />
    </beans:bean>

    <beans:bean id="authenticationFailureHandler" class="common.webapp.filter.ExtendedAuthenticationFailureHandler">
        <beans:property name="exceptionMappings">
            <beans:props>
                <beans:prop key="org.springframework.security.authentication.DisabledException">/login/accountDisabled</beans:prop>
                <beans:prop key="org.springframework.security.authentication.LockedException">/login/accountLocked</beans:prop>
                <beans:prop key="org.springframework.security.authentication.AccountExpiredException">/login/accountExpired</beans:prop>
                <beans:prop key="org.springframework.security.authentication.CredentialsExpiredException">/login/credentialsExpired</beans:prop>
                <beans:prop key="org.springframework.security.authentication.BadCredentialsException">/login/badCredentials</beans:prop>
            </beans:props>
        </beans:property>
    </beans:bean>

    <beans:bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />

</beans:beans>

これに対して、Javaファイルは下記の通り。

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/images/**", "/scripts/**", "/styles/**",
                                 "/error", "/login*/**", 
                                 "/signup*").permitAll()
                .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler())
                .permitAll()
            )
            .rememberMe(remember -> remember
                .userDetailsService(userDetailsService)
                .key("aaa")
            )
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID", "aaa")
            );

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public PasswordEncoder passwordTokenEncoder() {
        return new BCryptPasswordEncoder();
    }

    AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new ExtendedAuthenticationSuccessHandler("/top");
    }

    AuthenticationFailureHandler authenticationFailureHandler() {
        // 例外マッピングの設定
        Properties mappings = new Properties();
        mappings.put(DisabledException.class.getName(), "/login/accountDisabled");
        mappings.put(LockedException.class.getName(), "/login/accountLocked");
        mappings.put(AccountExpiredException.class.getName(), "/login/accountExpired");
        mappings.put(CredentialsExpiredException.class.getName(), "/login/credentialsExpired");
        mappings.put(BadCredentialsException.class.getName(), "/login/badCredentials");

        ExtendedAuthenticationFailureHandler handler = new ExtendedAuthenticationFailureHandler();
        handler.setExceptionMappings(mappings);
        return handler;
    }
}

Java 25のパフォーマンス改善点で気になったところ

特に気になった点だけ調べてみた。

ウォームアップ短縮(JEP 515)

Java アプリケーションは JIT コンパイルによって実行速度を最適化するが、その「最適化」に到達するまでのウォームアップ時間は長年の課題だった。特に短命なサービスやバースト型のワークロードでは、起動直後のレスポンス低下が無視出来ない。

JEP 515 では、過去の実行から収集したプロファイル情報を AOT キャッシュとして保存し、次回の起動時に利用できる仕組みが導入された。これにより、

  • 初期起動から JIT 最適化が効いた状態に近いパフォーマンスを発揮
  • マイクロサービスやサーバレス環境の「コールドスタート問題」を緩和
  • ウォームアップを待たずにスループットやレイテンシが安定

といったメリットがある。

トレーニング実行(プロファイル収集)

まずはアプリを「本番に近い負荷テスト」、「代表的な入力データ」等で実行し、JIT がどのメソッドをよく使うかを記録する。

java -XX:AOTCacheOutput=app-prof.aot -jar myapp.jar

本番実行(プロファイル利用)

保存したプロファイルを読み込み、本番環境で起動する。

java -XX:AOTCache=app-prof.aot -jar myapp.jar

Compact Object Headers(JEP 519)

Java のオブジェクトはヒープ上で管理され、各オブジェクトには「ヘッダ情報」が付与されている。従来は 12 バイトが標準だったが、JEP 519 では ヘッダを 8 バイトに圧縮するオプションが提供された。

これにより、

  • メモリ削減:多数の小さなオブジェクトを扱うアプリで特に有効。ヒープ効率が上がるため GC 回数も減少する。
  • キャッシュ効率の改善:メモリ占有が減ることで CPU キャッシュに収まるデータ量が増え、アクセスレイテンシが低下する。
  • スループット向上

利用方法は、JVM 起動時に以下を指定する。

java -XX:+UseCompactObjectHeaders -jar myapp.jar

デフォルトでONになっていないということは、全ての環境で改善するわけではないことを表している。

  • COH ではオブジェクトヘッダに格納されていた情報を圧縮/再配置するため、hashCode/同期多用のアプリでは遅くなる可能性
  • ヒープダンプ解析ツールや JVMTI を使った低レベルの計測では、
    オブジェクトレイアウトが従来と変わるため、互換性のリスクあり
  • メモリ帯域が十分に広いサーバー、CPU キャッシュが大きい最新世代のCPUでは効果が限定的になる場合がある(逆に、組込み系やクラウドVMのようにメモリ制約がある環境で効果大)

適用シナリオ

これらの最適化はすべてのアプリケーションで均等に効くわけではない。効果が大きいシナリオは以下の通りとなる。

ウォームアップ短縮(JEP 515)

  • サーバレス、マイクロサービス
  • 短命な CLI ツールやバッチ処理

Compact Object Headers(JEP 519)

  • ヒープ上に数百万単位のオブジェクトを保持するシステム
  • 高トラフィックの Web サービス
  • データ処理や分析基盤

まとめ

Java 25 の JEP 515 と JEP 519 は、単なる言語仕様の追加ではなく、実行基盤のボトルネックを狙い撃ちした改善となっている。
起動直後のパフォーマンスが課題であればウォームアップ短縮を、ヒープ使用量やGCコストに悩んでいるなら Compact Object Headers を試す価値がある。

YOLOでYouTubeの映像から物体検出する

YouTubeのライブ映像を取り込んで物体検出してみる。応用すると風景のライブ映像に野生動物が写ったらアラートを出す。録画するなど出来ると思う。

前回の下記部分のカメラ映像読み込み部分をYouTubeに置き換える。

# Load image source
cap = cv2.VideoCapture(0)

前回のパッケージに加えて「pip install yt-dlp」を実行しておく。

実装

モジュールの読み込み
from yt_dlp import YoutubeDL
YouTubeのライブ配信ページURLから、再生用の実URL(多くはHLSのm3u8)を取得する関数
def get_best_live_url(youtube_url: str) -> str:
    ydl_opts = {
        'quiet': True,
        'no_warnings': True,
        'skip_download': True,
        # Filters in order of ease of live performance
        'format': 'best[protocol^=m3u8]/best[ext=mp4]/best'
    }
    with YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(youtube_url, download=False)
        # `url` contains a URL that can be played directly (assuming m3u8)
        stream_url = info.get('url')
        if not stream_url:
            for f in info.get('formats', []):
                if 'm3u8' in (f.get('protocol') or ''):
                    stream_url = f.get('url')
                    break
        if not stream_url:
            raise RuntimeError('Unable to get live playback URL.')
        return stream_url
YouTubeの動画、ライブ配信を開く関数
def open_live_capture(stream_url: str) -> cv2.VideoCapture:
    cap = cv2.VideoCapture(stream_url)
    # Latency reduction (enabled builds only)
    try:
        cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
    except Exception:
        pass
    return cap
前回コードの変更部分

前回のコードの「cap = cv2.VideoCapture(0)」を下記に置き換える。「youtube_url」に取り込みたい動画やライブ映像のURLを入力する。

youtube_url = 'https://www.youtube.com/watch?v=好きな動画のURL'

stream_url = get_best_live_url(youtube_url)
print('stream:', stream_url)
cap = open_live_capture(stream_url)

if not cap.isOpened():
    raise RuntimeError('Failed to open VideoCapture. Please use an FFmpeg-enabled build of OpenCV.')

Android 16の新機能について

自分のスマホにAndroid 16がそろそろ配信されそうなため、主にプライバシー、パフォーマンス、バッテリー最適化、ゲーム関連について調べてみた。

プライバシーとセキュリティの強化について

1. アプリの権限リクエストがより細分化・透明化

写真・動画へのアクセス制限の強化

これまで「すべての写真を許可/拒否」のような大まかな選択肢しかなかったストレージアクセスに対して、Android 16では:

  • 特定の写真のみ選択的に許可
  • アクセス履歴の表示
  • 一時的なアクセス(ワンタイムパーミッション)

など、ユーザーがどのデータに対して、どのアプリに許可したかを明確に管理できるようになる。

位置情報とマイク・カメラの利用制限

  • 「常時許可」は廃止され、アプリがアクティブな時だけ許可が基本に
  • バックグラウンドでのマイク・カメラの使用はユーザーに通知
  • 通知バーで現在利用中のセンサー(マイク、カメラ、GPS)がリアルタイムで表示

2. Google Play プロテクトの進化(リアルタイムAIスキャン)

Android 16では、Google Play プロテクトにAIによるリアルタイム挙動監視機能が追加される。

  • 新規インストール時にアプリのコードや動作傾向をクラウドAIが即座に解析
  • 不審な動作がある場合は即時警告・ブロック
  • 「このアプリは類似アプリと比べて異常に多くのデータを送信しています」など、行動ベースで通知

これにより、インストール前だけでなく使用中のセキュリティも常時監視されるようになる。

3. システムアップデートの分割と即時性向上(モジュラー化)

Android 16では「Project Mainline」がさらに進化し、以下のような構成に:

  • セキュリティコンポーネントがOSと切り離され、Google Play経由で個別更新が可能
  • 端末メーカーやキャリアのアップデート配信を待たずに、月次パッチレベルで配布
  • 更新は自動かつバックグラウンドで完了

これにより、ゼロデイ攻撃への即応性が格段に上がるとされる。

4. アプリトラッキング・クロスアプリ識別子の制限

Appleの「App Tracking Transparency」に近い機能がAndroid 16でも強化されている。

  • アプリごとの広告ID取得がデフォルトで無効化
  • アプリがユーザーを他のアプリやサービスを横断して追跡する行為に対し、事前に明確な許可を求めるUIが表示
  • 許可履歴やデータ共有先の表示がユーザー向けに視覚化

これにより、「勝手に広告が最適化される」不快感を低減する。

5. プライバシーダッシュボードの強化

「設定」→「プライバシー」にあるダッシュボード画面が大幅に刷新された。

  • 各アプリがいつ、何のデータにアクセスしたかを時系列で表示
  • 「カレンダーへのアクセス:○月×日 14:22」など具体的な操作ログ付き
  • 不要と判断したアクセスは即座に取り消し可能

パフォーマンスとバッテリー最適化について

OSレベルでの最適化と、AIによる動的制御を組み合わせた省電力かつ高速なシステム設計が実現される。特に、バックグラウンド処理やメモリ割り当て、充電中の動作制御に重点が置かれている。

1. 高効率スケジューラ:Dynamic Task Prioritization(DTP)

Android 16のカーネルスケジューラは、タスク実行時にアプリの重要度や使用頻度を学習し、CPU・GPUリソースを動的に割り当てる機構が導入された。

  • アクティブアプリの処理を即時優先
  • バックグラウンドアプリは低負荷スレッドに回される
  • AIがユーザーの利用傾向を継続的に学習し、最適な実行タイミングを決定

これにより、操作時のラグ減少や描画高速化が実現しつつ、リソースの無駄遣いも抑制される。

2. バックグラウンド処理の制限強化

Android 16では、Dozeモード/App Standby Bucket(ASB)/Foreground Serviceの動作制御が進化している。

  • アプリを「Active/Working set/Frequent/Rare/Restricted」に自動分類
  • 端末の未使用時間が長いほど、該当アプリのバックグラウンド実行権限が縮小
  • バッテリー消費の激しいアプリに対し、強制スリープ処理を自動で適用

また、ユーザーは「バッテリー使用量ランキング」から該当アプリの活動詳細を確認し、手動制御も可能となる。

3. AIによる電力最適化:Adaptive Battery v2

Android 16では「Adaptive Battery」が第2世代にアップグレードされ、ディープラーニングによる予測制御が導入された。

  • アプリごとの使用傾向を解析し、不要なプロセスの事前抑制
  • 毎日の行動パターンに基づき、電力供給を時間帯ごとに最適配分
  • 充電パターンを学習してバッテリー劣化を抑える

たとえば、就寝中の充電では、急速充電を避けて「夜間スロー充電モード」に自動切り替えされる。

4. バッテリー保護機能の強化

Android 16対応端末では、以下のようなハードウェア連携機能もサポートされる。

  • 温度・電圧監視機構の統合により、過熱や過充電をOSが自動制御
  • 一定の温度や電圧条件を超えると、充電速度を一時的に抑制
  • 「バッテリー寿命延長モード」では、満充電を80〜85%で制御するオプションも搭載

これにより、バッテリー膨張や性能劣化のリスクを抑え、3〜5年の長期使用を前提とした保護設計が可能になる?

5. 開発者向け電力監視ツールの拡充

アプリ開発者向けに提供されるツールも進化した。

  • Android Studio Profilerでの電力消費分析が高精度化
  • 各フレームごとのCPU負荷、GPU描画負荷、ネットワーク使用率、バッテリー消費をリアルタイムで可視化
  • 開発段階で省電力設計を意識したUI設計が可能

さらに、Battery Usage Stats APIの改良により、アプリ内部からも消費電力量を把握・最適化できる仕組みが追加される。

ゲーム・マルチメディア機能の進化について

Android 16では、モバイルデバイスを「ゲーム機」、「エンタメプレイヤー」として本格的に使うための土台が大幅に強化された。特にGPU制御、オーディオ出力、ディスプレイ対応が進化し、高性能端末のポテンシャルを最大限に引き出せる設計としている。

1. グラフィックスと描画性能の最適化

Vulkan APIのさらなる強化

  • Android 16ではVulkan 1.3 APIに対応(端末依存あり)
  • アセットの事前読み込み(Pipeline Caching)により読み込み時間を短縮
  • メッシュシェーダーや拡張バッファ制御によって、3D表現のリアリティ向上
  • GPUとCPUの同期処理の遅延(frame pacing)を大幅に削減

これにより、FPSやオープンワールド系ゲームでも滑らかで安定した描画が可能になり、90fpsや120fps表示にも対応しやすくなる。

ハードウェアレベルのレイテンシー改善

  • Graphics HAL(ハードウェア抽象化レイヤ)の改良により、タッチ入力→描画までの遅延を最小化

高速表示が求められるゲームでも、瞬時の反応性が得られる。

2. ゲームダッシュボードの標準搭載

従来はSamsungやASUSなどの一部メーカーUIに限られていた「ゲームツール機能」が、Android 16では標準化された。

  • フレームレート(FPS)モニタリング
  • スクリーンレコード(録画)やライブ配信
  • 通知の一時ブロック
  • タッチ感度や視野角の即時調整
  • リソース節約モードへの切り替え

これにより、ゲーム中のUX改善と集中力維持がしやすくなる。

3. コントローラー対応と操作性の強化

Android 16ではBluetooth HIDプロファイルが拡張され、DualSense(PS5)やXboxコントローラーの高精度入力に正式対応する。

  • アナログトリガー、触覚フィードバック、ジャイロセンサーなども一部ゲームでサポート
  • 外付けゲームパッドやUSB-C接続型ゲームデバイスとのレイテンシー最小化

さらに、画面操作においてもスワイプ範囲のカスタマイズや、指の誤反応防止のタッチ制御アルゴリズムが強化された。

4. マルチメディア:映像と音響の進化

映像体験の進化

  • HDR10+、Dolby Vision、HLG などの最新HDR規格に幅広く対応
  • 輝度レンジ、色域、階調表現が向上し、映画・配信視聴での没入感アップ
  • ハードウェア対応端末では、自動トーンマッピング/暗部補正などのリアルタイム補正が可能

オーディオ機能の進化

  • Spatial Audio(空間オーディオ):対応コンテンツで立体音響再生が可能に
  • 360度の音場感覚
  • 頭の動きに追従する「ヘッドトラッキング機能」にも対応(対応イヤホン必要)

Bluetooth LE Audio対応:

  • 超低遅延通信
  • LC3コーデックによる高音質+省電力
  • 複数デバイス同時接続(ブロードキャスト)への拡張

これにより、ゲーム・動画どちらにおいてもプロレベルのオーディオ体験が可能となる。

5. XR(AR/VR)デバイスへの基盤整備

Android 16では、今後の拡張現実(XR)プラットフォーム展開を見据えて、

  • Googleの新しいARフレームワーク「ARCore 4.0」に最適化
  • 空間マッピングや深度認識の精度向上
  • ヘッドセット連携に向けた低遅延ビデオ出力APIを統合

Meta QuestやSnapdragon Spacesなどの外部XRエコシステムとの統合も視野に入れた設計となっており、将来的にはAndroid端末をXRハブとして活用する流れも予想される。

Spring Data JPA 3.5でSpecification.whereが非推奨になった件

/**
 * Simple static factory method to add some syntactic sugar around a {@link Specification}.
 *
 * @apiNote with 4.0, this method will no longer accept {@literal null} specifications.
 * @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
 * @param spec can be {@literal null}.
 * @return guaranteed to be not {@literal null}.
 * @since 2.0
 * @deprecated since 3.5, to be removed with 4.0 as we no longer want to support {@literal null} specifications.
 */
@Deprecated(since = "3.5.0", forRemoval = true)
static <T> Specification<T> where(@Nullable Specification<T> spec) {
    return spec == null ? (root, query, builder) -> null : spec;
}

非推奨になったけれども、まだ代替手段はないようだ。Nullを許容しなくなることを警告するためのものらしい。

The where method merely caters for the broken nullability allowance and is replaced by where(PredicateSpecification) in 4.0.

まだ確定ではないかもしれないが、mainでは下記の様に修正されている。

/**
 * Simple static factory method to add some syntactic sugar translating {@link PredicateSpecification} to
 * {@link Specification}.
 *
 * @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
 * @param spec the {@link PredicateSpecification} to wrap.
 * @return guaranteed to be not {@literal null}.
 */
static <T> Specification<T> where(PredicateSpecification<T> spec) {

    Assert.notNull(spec, "PredicateSpecification must not be null");

    return (root, update, criteriaBuilder) -> spec.toPredicate(root, criteriaBuilder);
}

データベース上に「Null」が登録されていることと、Java変数が「Null」であることは意味合いが違うから、そのまま「Null」を渡すことをNGとした感じだろうか。

@Deprecatedを付けるのはまだ早かったのではないだろうか。(同じようにNullを許容しなくなる他のメソッドにはついていないし)

Stable Diffusion WebUI Forge版をインストールする

概要

Stable Diffusion WebUI Forge版は、オリジナル「Automatic1111 Stable Diffusion WebUI」を土台としつつ、開発・運用をより快適にするための最適化と実験的機能を盛り込んだもの。

特徴

  • 高速化・低メモリ化
  • 各種バックエンド対応
  • 一括インストーラ(Windows向け)
  • 実験的機能の研究プラットフォーム

インストール(Windows向け)

gitもpythonも事前インストールは不要。全て含まれているファイルが提供されている。

以下のページにアクセスして、

https://github.com/lllyasviel/stable-diffusion-webui-forge

下記のファイルをダウンロードする。

Just use this one-click installation package (with git and python included).
>>> Click Here to Download One-Click Package (CUDA 12.1 + Pytorch 2.3.1) <<<

任意のフォルダに解答したら、update.batを実行する(初回のみ)。

処理が終わったら、run.batを実行する。

Ultralytics YOLOで物体検出する

目的

カメラ映像にリアルタイムで物体名をラベル付けする。

環境

Winodows 11(Ubuntuでも動いた。Ubuntu on Raspberry Pi 4でも動いた。)
Python 3.12
PyTorch(指定したバージョンのCUDAを使用したいなど、理由があれば先に入れておく。後続の作業で自動的にインストールすることもできるので、必要に応じて)

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126

ライセンス

Ultralytics YOLOを無償で使うならAGPL-3.0 Licenseになる。

AGPL-3.0 License: This OSI-approved open-source license is perfect for students, researchers, and enthusiasts. It encourages open collaboration and knowledge sharing. See the LICENSE file for full details.

仮想環境を作成してインストール

mkdir yolo
cd yolo
python -m venv --system-site-packages venv

venv\Scripts\activate

pip install ultralytics

動作確認

yolo detect predict model=yolo11n.pt

Ultralytics 8.3.103 🚀 Python-3.12.9 torch-2.6.0+cu126 CUDA:0 (NVIDIA GeForce RTX 4060 Ti, 16380MiB)
YOLO11n summary (fused): 100 layers, 2,616,248 parameters, 0 gradients, 6.5 GFLOPs

image 1/2 D:\yolo\venv\Lib\site-packages\ultralytics\assets\bus.jpg: 640×480 4 persons, 1 bus, 44.3ms
image 2/2 D:\yolo\venv\Lib\site-packages\ultralytics\assets\zidane.jpg: 384×640 2 persons, 1 tie, 45.9ms
Speed: 3.5ms preprocess, 45.1ms inference, 62.8ms postprocess per image at shape (1, 3, 384, 640)
Results saved to runs\detect\predict

※以下はRaspberry Pi 4で動かしたときの実行結果。(さすがに遅い)

Ultralytics 8.3.105 🚀 Python-3.12.3 torch-2.6.0+cpu CPU (Cortex-A72)
YOLO11n summary (fused): 100 layers, 2,616,248 parameters, 0 gradients, 6.5 GFLOPs

image 1/2 /home/xxx/yolo/venv/lib/python3.12/site-packages/ultralytics/assets/bus.jpg: 640×480 4 persons, 1 bus, 1180.9ms
image 2/2 /home/xxx/yolo/venv/lib/python3.12/site-packages/ultralytics/assets/zidane.jpg: 384×640 2 persons, 1 tie, 935.2ms
Speed: 16.4ms preprocess, 1058.1ms inference, 6.1ms postprocess per image at shape (1, 3, 384, 640)
Results saved to runs/detect/predict

※Raspberry Pi 5ならNCNNを使用することも検討。Pi 4ではncnnのエクスポートがエラーになって動かなかった。
pip install ncnn
yolo export model=yolo11n.pt format=ncnn

実装

こちら(https://www.ejtech.io/learn/yolo-on-raspberry-pi)を参考にさせて頂いた。

以下のコードではyolo11lを使っている。Raspberry Piで実行するならyolo11nかyolo11sのどちらかのみになるかと思う。
こちら(https://docs.ultralytics.com/ja/models/yolo11/)からダウンロードすることが出来る。

import time

import cv2
import numpy as np

from ultralytics import YOLO

# Load the model into memory and get labemap
model = YOLO('yolo11l.pt', task='detect')
labels = model.names

# Load image source
cap = cv2.VideoCapture(0)

# Set bounding box colors (using the Tableu 10 color scheme)
bbox_colors = [(164,120,87), (68,148,228), (93,97,209), (178,182,133), (88,159,106), 
              (96,202,231), (159,124,168), (169,162,241), (98,118,150), (172,176,184)]

# Initialize control and status variables
avg_frame_rate = 0
frame_rate_buffer = []
fps_avg_len = 200

# Begin inference loop
while True:

    t_start = time.perf_counter()
    # Load frame from image source
    ret, frame = cap.read()
    if (frame is None) or (not ret):
        print('Unable to read frames from the camera. This indicates the camera is disconnected or not working. Exiting program.')
        break

    # Run inference on frame
    results = model(frame, verbose=False)

    # Extract results
    detections = results[0].boxes

    # Initialize variable for basic object counting example
    object_count = 0

    # Go through each detection and get bbox coords, confidence, and class
    for i in range(len(detections)):

        # Get bounding box coordinates
        # Ultralytics returns results in Tensor format, which have to be converted to a regular Python array
        xyxy_tensor = detections[i].xyxy.cpu() # Detections in Tensor format in CPU memory
        xyxy = xyxy_tensor.numpy().squeeze() # Convert tensors to Numpy array
        xmin, ymin, xmax, ymax = xyxy.astype(int) # Extract individual coordinates and convert to int

        # Get bounding box class ID and name
        classidx = int(detections[i].cls.item())
        classname = labels[classidx]

        # Get bounding box confidence
        conf = detections[i].conf.item()

        # Draw box if confidence threshold is high enough
        if conf > 0.5:

            color = bbox_colors[classidx % 10]
            cv2.rectangle(frame, (xmin,ymin), (xmax,ymax), color, 2)

            label = f'{classname}: {int(conf*100)}%'
            labelSize, baseLine = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) # Get font size
            label_ymin = max(ymin, labelSize[1] + 10) # Make sure not to draw label too close to top of window
            cv2.rectangle(frame, (xmin, label_ymin-labelSize[1]-10), (xmin+labelSize[0], label_ymin+baseLine-10), color, cv2.FILLED) # Draw white box to put label text in
            cv2.putText(frame, label, (xmin, label_ymin-7), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) # Draw label text

            # Basic example: count the number of objects in the image
            object_count = object_count + 1

    # Calculate and draw framerate (if using video, USB, or Picamera source)
    cv2.putText(frame, f'FPS: {avg_frame_rate:0.2f}', (10,20), cv2.FONT_HERSHEY_SIMPLEX, .7, (0,255,255), 2) # Draw framerate
    
    # Display detection results
    cv2.putText(frame, f'Number of objects: {object_count}', (10,40), cv2.FONT_HERSHEY_SIMPLEX, .7, (0,255,255), 2) # Draw total number of detected objects
    cv2.imshow('YOLO detection results',frame) # Display image

    # If inferencing on individual images, wait for user keypress before moving to next image. Otherwise, wait 5ms before moving to next frame.
    key = cv2.waitKey(5)
    
    if key == ord('q') or key == ord('Q'): # Press 'q' to quit
        break
    elif key == ord('s') or key == ord('S'): # Press 's' to pause inference
        cv2.waitKey()
    elif key == ord('p') or key == ord('P'): # Press 'p' to save a picture of results on this frame
        cv2.imwrite('capture.png',frame)
    
    # Calculate FPS for this frame
    t_stop = time.perf_counter()
    frame_rate_calc = float(1/(t_stop - t_start))

    # Append FPS result to frame_rate_buffer (for finding average FPS over multiple frames)
    if len(frame_rate_buffer) >= fps_avg_len:
        temp = frame_rate_buffer.pop(0)
        frame_rate_buffer.append(frame_rate_calc)
    else:
        frame_rate_buffer.append(frame_rate_calc)

    # Calculate average FPS for past frames
    avg_frame_rate = np.mean(frame_rate_buffer)

# Clean up
print(f'Average pipeline FPS: {avg_frame_rate:.2f}')
cap.release()
cv2.destroyAllWindows()

Modelをyolo11n.ptにしてRaspberry Pi 4でも動かしたが、0.9 FPSしか出なかった。Raspberry Pi 5やRaspberry Pi AI HAT+が欲しくなる。

probably due to unaligned versions of the junit-platform-engine and junit-platform-launcher jars on the classpath/module path.が出るようになった件

EclipseでJunitを実行したら、「probably due to unaligned versions of the junit-platform-engine and junit-platform-launcher jars on the classpath/module path.」とエラーが出るようになった。

Junit 5.12からは、launcherのバージョンを明示的に指定してクラスパスに通しておかないと、Eclipseで互換性のない古いバージョンが使われてしまい、エラーになる。

<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.12.1</version>
    <scope>test</scope>
</dependency>

Windows Storeからインストールした複数バージョンのpythonを切り替える

力業だが、絶対パスで直接仮想環境を作成する方法が早いかと思う。

AppData\Local\Microsoft\WindowsAppsにある各バージョンのPythonフォルダを参照しvenvを実行する。

C:\Users\xxxx\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.10_xxxxxxxxxxxx\python.exe -m venv D:\python310\venv

後は通常通りactivateすれば、作成した仮想環境を使用できる。

D:\python310\venv\Scripts\activate

(venv) D:\>python --version
Python 3.10.11

stable diffusionのPythonバージョンを指定する方法もこれで出来る。

初回起動時、webui-user.batを実行する前に下記を実行する。(既に実行してしまっている場合はインストールフォルダ下のvenvを一旦削除する)

C:\Users\xxxx\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.10_xxxxxxxxxxxx\python.exe -m venv 「stable diffusionのインストールフォルダ」\venv

後は通常通りwebui-user.batを実行する。

Android 15の新機能について

最近やっと自分のスマホにAndroid 15が配信されたので、自分に恩恵のありそうな機能について調べてみた。

デバイス追跡・盗難防止機能について

1. 盗難検知ロック(Theft Detection Lock)

この機能は、AIとデバイス内のセンサー(加速度センサーやジャイロスコープ)を活用し、スマートフォンがユーザーの手からひったくられるなどの不審な動きを検知する。検知すると、画面が自動的にロックされ、第三者による不正アクセスを防止する。

デバイスの通常の使用中に中断されることを最小限に抑えるため、以下の状況では盗難検出ロックが作動しないことがある。

  • デバイスの Wi-Fi 接続と Bluetooth 接続の一方または両方が安定している場合
  • 短時間にロックが繰り返された場合

2. オフラインデバイスロック(Offline Device Lock)

デバイスが長時間オフライン状態(機内モードやネットワーク接続の切断)にある場合、自動的に画面をロックする機能。これにより、盗難者がデバイスをオフラインにして追跡を回避しようとする行為を防ぐ。

3. リモートロック(Remote Lock)

ユーザーは、任意のデバイスから android.com/lock にアクセスし、認証済みの電話番号を使用して、紛失または盗難されたスマートフォンを遠隔でロックできる。これにより、Googleアカウントのパスワードを覚えていなくても、デバイスを迅速に保護することが可能。

4. 出荷時設定へのリセット保護

盗難者がデバイスを初期化して再利用するのを防ぐため、所有者のパスワードなしで工場出荷時の設定にリセットできないようにする保護機能が強化されている。

処理速度の向上について

  1. 16KBページサイズのサポート: 従来の4KBから16KBのメモリページサイズに対応することで、ページテーブルの参照回数が減少し、全体的なパフォーマンスが5~10%向上する。ただし、メモリ使用量は約9%増加する。
  2. メモリ管理アルゴリズム「MGLRU」の導入: 新しいメモリ管理アルゴリズム「MGLRU」を採用することで、メモリ管理が効率化され、アプリの起動時間短縮やシステム全体のパフォーマンス向上が期待できる。
  3. アプリ起動時の最適化: 16KBページサイズの採用により、メモリ圧迫時のアプリ起動時間が平均3.16%短縮され、一部のアプリでは最大30%の改善が見られる。

バッテリー効率の改善

  1. ドーズモードへの移行速度の向上: デバイスがアイドル状態になるとバッテリー消費を抑える「ドーズモード」への移行が50%高速化され、一部のデバイスではスタンバイ時のバッテリー寿命が最大3時間延長される。
  2. 80%充電制限機能の導入: バッテリーの劣化を防ぐため、充電を80%で自動的に停止する新機能がテストされている。これにより、バッテリーの長寿命化が期待できる。
  3. フォアグラウンドサービスの管理強化: アクティブに動作し続けるアプリを厳密に管理することで、不要なバッテリー消費を削減し、全体的なバッテリー効率を向上させている。

感想

盗難検知ロックについてはデフォルトでONにしても良いのではないかと思う。

処理速度の向上については、それほど大きなインパクトはない。特にゲームは最大限の恩恵を受けるには、新しいVulkan APIなどの対応状況も重要な要素となる。