Quantcast
Channel: 午後わてんのブログ
Viewing all 420 articles
Browse latest View live

トマト去年一昨年との比較、いちごの葉っぱ大量処分、ヤクシマルリシジミの幼虫見当たらない、土の熱消毒その後

$
0
0


2017/06/28
トマト(レッドオーレ)種まきから61日目、開花から14日目
イメージ 1

一昨日
イメージ 2
ほとんど雨か曇りの3日間だった

主枝の第一花房(開花から14日目)
イメージ 3
花はすべて終了している
右に伸びているのが第一側枝

主枝の第二花房
イメージ 4
半分くらい咲き終わった
主枝はここで摘心
左上に伸びているのが第二側枝

第一側枝の第一花房
イメージ 5

第一側枝の第二花房
イメージ 6
開花間近

第二側枝の第一花房
イメージ 7
開花はまだ先だねえ

葉柄の根本が折れた葉っぱ萎れる
イメージ 18
この前は平気そうだったんだけど萎れてきたので
イメージ 19
切り取った

去年や一昨年と比べてみる
イメージ 8


去年の6/2
種まきから61日目
イメージ 9
ちょうど開花した日だった
ここから14日後の6/16の写真を探したけど
6/25まで携帯電話が故障中で撮影できていなかった

去年の6/28
イメージ 10
肥料が足りていない



一昨年の6/28、種まきから48日目
イメージ 11
定植直後かな
この青い鉢の株は肥料不足だったけど
この後は病気にならずよく育った

一昨年の61日目は7/11の様子
イメージ 12
全体的に肥料不足かなあ
こうして比べてみると今年はかなりいい調子だなあ
肥料大事

スイートバジル一昨日
イメージ 13

今日
イメージ 14
高さは50センチくらい
脇芽から伸びた枝の先にも花芽が出てきているので
種を収穫しないならこの時点で全て収穫するのが良さそう
5/5の種まきから54日目

収穫の終わった株を引き抜く
イメージ 16
イメージ 15
株との距離が近いと大きくならない

イメージ 17
強風に当たった後は極端に縮れた葉っぱが出てくる


いちごのサマーカット?
イメージ 20
かなり窮屈そう
イメージ 21
新しく出てきた葉っぱ
最近はきれいな葉っぱが出てくるようになった

全体
イメージ 43
去年までは放置していたけど

古い葉っぱを取り除いてみた
イメージ 42
取りすぎたかなあと思いつつ

二日後の今日
イメージ 22
新しい葉っぱが元気に出てきているみたい

いちごは株自体も増える?
イメージ 23
去年植えたときは一つの株だったんだけど
3つに増えているみたい

取り除いた葉っぱ、裏表
イメージ 41
大きいのが一番古い葉っぱで開花直前に出てきたんだったかなあ
開花から収穫終わるまでは新しく葉っぱは出てこなくて
その後に出てきた葉っぱが小さくて黒くて縮れたもの
土は熱消毒したものを使ったからセンチュウ類は少ないはずなので
原因はアザミウマ類だと思う



ヤクシマルリシジミの幼虫
一昨日
イメージ 24
居る

昨日
イメージ 25
居る

この前はここにも居たんだけど
イメージ 26
下から見ても
上から見ても
イメージ 27
見当たらない

今日
イメージ 28
卵の殻はあるけど

イメージ 46
幼虫は見当たらない

居ない
イメージ 40
卵は合計で15個くらい確認したはずなんだけど
今まで葉っぱが食べられた様子もまったくない
いちごの葉っぱではヤクシマルリシジミは育たないのかなあ




レッドオーレ9日目で発芽、オオマツバウンランの種まき、いちご赤くなってきた ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14900919.html
より土の熱消毒のつづき
5/6と随分前になるけどこれが

5/18
イメージ 29
天気のせいもあって乾燥まで結構かかった、12日
水浸しになっていたときはベタベタだったけど
乾燥したら発泡スチロールみたいな軽さになった

イメージ 30
これは終わって

次、まだある
イメージ 31
残り少ないから袋ごと持ち上げようとしたら破れた
一応日陰においていたんだけど2年は長かったかな

イメージ 32
25リットルでも持った感じ重さ10キログラムも無いくらいだったから
自転車の籠に入れて持ち帰れたんだよなあ
このときは水浸しでタプタプ

イメージ 33
なんとか運んできて中身をぶちまけた

改めて袋の注意書きを見る
イメージ 34
"開封後は雨の当たらないところに保存してください"

イメージ 35
菌根菌とか書いてあるけど全滅しているだろうし
残っていたとしてもこれからの熱消毒で死滅しちゃう

臭い
イメージ 36
牛ふん堆肥の臭いをきつくした感じ
と言うより牛糞の臭いかも?未熟な堆肥だったのかなあ
臭い

イメージ 37
設置完了
から13日後
イメージ 44
できた
乾燥すると本当に軽いふわふわのパサパサ

イメージ 45
ようやく懸念が払拭された
この後は普通の土を混ぜて使うことになる
今のトマトに使っているのがそれ

日焼け
イメージ 38
今年は6月より5月のほうが晴れが多くて暑かったかなあ
去年もそうだったかな

去年からアームカバーが活躍
イメージ 39
軍手は写真が撮りづらいからあまり使わないせいで
手首に日焼けの境目ができる


前回の記事
不調なトマトの摘心、強風の被害のその後、スイートバジルの花芽、ヤクシマルリシジミの幼虫確認 ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14990984.html




WPF、変形した要素を指定位置に移動、NotifyProperty

$
0
0

要素を指定した位置に移動させる
イメージ 1
 水色の四角枠は目印、サイズ100x100、x,y=100,100
赤の四角が目的の要素、サイズ100x100
移動ボタン1と2どちらも100,100へ移動させるボタンだけど基準が違う
基準A:ボタン1は見た目上の位置
基準B:ボタン2は内部的な位置

変形後も内部の位置やサイズは変化しないから見た目とズレるから、このズレを計算しての位置指定
変形後の要素を他の要素にピッタリ重ねたいとか、横にくっつけたいとかしたいので基準Aの方法が必要だった
x,y=100,100のとき20度回転させると
イメージ 2
x,y=85.9,85.9にズレる
これを100,100にするにはズレの分だけずらせばいい
ズレは-14.1なので
100+(-(-14.1))=114.1
イメージ 3
100,100ぴったりになった



要素の中心を軸に回転させるとピッタリ収まる四角枠は位置もサイズも変化する

今回の赤の四角はThumbなんだけど
Canvasを入れたControlTemplateを作ってそれをThumbのTemplateに指定して、そのCanvasに赤のBorderを入れている
Thumb.Template
┗Canvas
┗Border
変形させているのはTemplateのCanvas



用意したPriorityは全部NotifyPriority通知プロパティにした
MyLeftX座標、左位置
MyTopY座標、上位置
MyAngle回転角度
DiffPoint見た目と中身の位置の差分
OutSize見た目の大きさ、ぴったり枠のサイズ
MyOutBoundsぴったり枠の位置とサイズ



TransformToVisualとTransformBoundsを使うと要素の変形後のRect(サイズや位置)を取得することができる

Dim gt As GeneralTransform = 要素.TransformToVisual(今回はThumb)
Dim r As Rect = gt.TransformBounds(New Rect(New Size(要素.Width, 要素.Height)))
イメージ 4
この場合だと位置はズレの値(Thumbとの相対的な位置)になっている(-14.05…)
これをDiffPointに入れておいて
サイズ(128.17…)はOutSizeに入れておく



変形(回転)させたらぴったり枠も更新するので
MyAngleのSetのところでそれを実行する
イメージ 5
RootRotate.Angle=Valueが実際に回転指定しているところ


DiffPoint見た目と中身の位置の差分
OutSize見た目の大きさ、ぴったり枠のサイズ
を更新して
イメージ 6
MyOutBoundsぴったり枠の位置とサイズ
これも更新
イメージ 7



移動させたときは
MyOutBoundsの位置の更新だけなので
MyLeftとMyTopのSetのところで
イメージ 8
Canvas.SetLeft(Me, Value)が実際に位置指定しているところ
そのあとのCall SetOutBoundsで
イメージ 9
x,yだけ差分を足して、サイズはそのまま




ぴったり枠基準(基準A)で指定位置に移動
指定された値に差分を足した値を指定
なのでMainWindowからは単純に
MyExThumb.SetPoint2(100, 100)
だけで見た目上の100,100の位置に移動させることができる
イメージ 11




デザイン画面、XAMLを書くと投稿エラーになるから画像で
イメージ 10


VBコード
MainWindowとThumbを継承したExThumb


Imports System.ComponentModel
Imports System.Windows.Controls.Primitives


Class MainWindow

Private WithEvents MyExThumb As ExThumb

Private Sub MyCheck()
Dim root = MyExThumb.testRootCanvas
End Sub
Private Sub MyCheck2()
MyExThumb.SetPoint2(100, 100)
End Sub
Private Sub MyMove()
MyExThumb.MyLeft = 100
MyExThumb.MyTop = 100
End Sub
Private Sub MyMove2()
MyExThumb.SetPoint2(0, 0)
End Sub
Private Sub MyMove3()
MyExThumb.MyLeft = 0
MyExThumb.MyTop = 0
End Sub

'数値確認用のTextBlockへのBinding
Private Sub SetTextBlockBinding(so As Object, sName As String, tb As TextBlock)
Dim b As New Binding(sName) With {.Source = so, .StringFormat = sName & " = {0:0.0}"}
tb.SetBinding(TextBlock.TextProperty, b)
End Sub

Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
AddHandler btnCheck.Click, AddressOf MyCheck
'AddHandler btn1.Click, AddressOf MyMove2
AddHandler btn2.Click, AddressOf MyCheck2
'AddHandler btn3.Click, AddressOf MyMove3
AddHandler btn4.Click, AddressOf MyMove

'ExThumbに100x100の赤Borderを追加してMyCanvasに表示
Dim ext As New ExThumb(New Border With {
  .Width = 100, .Height = 100, .Background = Brushes.Red, .Opacity = 0.5})
Canvas.SetLeft(ext, 0) : Canvas.SetTop(ext, 0)
MyCanvas.Children.Add(ext)
MyExThumb = ext

'回転角度をSliderにBinding
Dim b As Binding
b = New Binding(NameOf(ExThumb.MyAngle)) With {.Source = MyExThumb, .Mode = BindingMode.TwoWay}
sldAngle.SetBinding(Slider.ValueProperty, b)

'数値確認用のTextBlockへのBinding
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyAngle), tbAngle) '角度
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.DiffPoint), tbRect) '差分座標
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyLeft), tbLeft) '実際のX座標
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyOutBounds), tbBounds) '見た目のピッタリ枠

End Sub

Private Sub MyExThumb_DragDelta(sender As Object, e As DragDeltaEventArgs) Handles MyExThumb.DragDelta
MyExThumb.MyLeft += e.HorizontalChange
MyExThumb.MyTop += e.VerticalChange
End Sub

End Class




Public Class ExThumb

Inherits Thumb 'Thumbを継承
Implements ComponentModel.INotifyPropertyChanged '通知プロパティ用
Private RootCanvas As Canvas
Private RootRotate As RotateTransform
Public testRootCanvas As Canvas

'OutBoundsの左上座標を指定
Public Sub SetPoint2(x As Double, y As Double)
MyLeft = x + (-DiffPoint.X)
MyTop = y + (-DiffPoint.Y)
End Sub

'DiffPointとOutBoundsの更新、変形時に実行する
Private Sub SetDiffPointAndOutSize()
Dim gt As GeneralTransform = RootCanvas.TransformToVisual(Me)
Dim r As Rect = gt.TransformBounds(New Rect(New Size(RootCanvas.Width, RootCanvas.Height)))
DiffPoint = r.Location
OutSize = r.Size
Call SetOutBounds()
End Sub

'OutBoundsの更新、移動時に実行する
Private Sub SetOutBounds()
Dim r As Rect = New Rect(New Point(DiffPoint.X + MyLeft, DiffPoint.Y + MyTop), OutSize)
MyOutBounds = r

End Sub


#Region "Property"

Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

Private Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberName> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

'変形前後の左上座標の差分
Private Property _DiffPoint As Point
Public Property DiffPoint As Point
Get
Return _DiffPoint
End Get
Set(value As Point)
_DiffPoint = value
Call NotifyPropertyChanged()
End Set
End Property
'要素がピッタリ収まるサイズ
Private Property _OutSize As Size
Public Property OutSize As Size
Get
Return _OutSize
End Get
Set(value As Size)
_OutSize = value
Call NotifyPropertyChanged()
End Set
End Property
'回転角度
Private Property _MyAngle As Double
Public Property MyAngle As Double
Get
Return _MyAngle
End Get
Set(value As Double)
_MyAngle = value
RootRotate.Angle = value
Call NotifyPropertyChanged()
Call SetDiffPointAndOutSize()
End Set
End Property
'X座標
Private Property _MyLeft As Double
Public Property MyLeft As Double
Get
Return _MyLeft
End Get
Set(value As Double)
_MyLeft = value
Canvas.SetLeft(Me, value)
Call NotifyPropertyChanged()
Call SetOutBounds()
End Set
End Property
'Y座標
Private Property _MyTop As Double
Public Property MyTop As Double
Get
Return _MyTop
End Get
Set(value As Double)
_MyTop = value
Canvas.SetTop(Me, value)
Call NotifyPropertyChanged()
Call SetOutBounds()
End Set
End Property
'要素がピッタリ収まる四角枠
Private Property _MyOutBounds As Rect
Public Property MyOutBounds As Rect
Get
Return _MyOutBounds
End Get
Set(value As Rect)
_MyOutBounds = value
Call NotifyPropertyChanged()

End Set
End Property

#End Region


'ControlTemplate作成、Canvasを一個入れるだけ
Private Function CreateTemplate() As ControlTemplate
Dim ct As New ControlTemplate(GetType(Thumb))
Dim c As New FrameworkElementFactory With {.Name = "RootCanvas", .Type = GetType(Canvas)}
ct.VisualTree = c
Return ct
End Function

'コンストラクタ
'渡された要素をTemplateの中のCanvasに追加する
Public Sub New(elm As FrameworkElement)
Template = CreateTemplate()
ApplyTemplate() 'Templateを再構築、必要
'TemplateのCanvasを取得して渡された要素を追加
RootCanvas = Me.Template.FindName("RootCanvas", Me)
With RootCanvas
.Children.Add(elm)
.Height = elm.Height
.Width = elm.Width
End With
testRootCanvas = RootCanvas 'test

'各種TransformをGroupにしてTemplateのCanvasのRenderTransformに指定
RootRotate = New RotateTransform
Dim sc As New ScaleTransform
Dim sk As New SkewTransform
Dim tg As New TransformGroup
With tg.Children
.Add(sc) : .Add(sk) : .Add(RootRotate)
End With
With RootCanvas
.RenderTransformOrigin = New Point(0.5, 0.5)
.RenderTransform = tg
.Background = Brushes.Transparent
End With

'ピッタリ枠とかを更新するために角度指定
MyAngle = 0.0

End Sub

End Class




前回までのDependencyPropertyを最初に使って試したけどうまく書けなくて
今回はNotifyPropertyっていう値が変更されたら通知を出せるプロパティを使った
うまくできなかったのは変形(Angleを変更)したときにTransformToVisualを使って変形後のRectを求めるところ、こんなふうに書いてみたけど
イメージ 12

イメージ 13

DependencyPropertyのPropertyMetadataのPropertyChangedCallbackのところでTransformToVisualを使ってみたんだけどこのタイミングだと変形する直前みたいで変形前のRectしか取得できなかった

なので今回のようにNotifyPropertyって言う値の変更時に通知を出せる通知プロパティを使った、これは一年前の方法とほとんど変わらないけど少しうまく書けた気がする


参照したところ
INotifyPropertyChanged インターフェイス (System.ComponentModel)
https://msdn.microsoft.com/ja-jp/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1

特に紫の文字のコトロがすごい便利だった

Private Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberName> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

これがないと例えば

'変形前後の左上座標の差分
Private Property _DiffPoint As Point
Public Property DiffPoint As Point
Get
Return _DiffPoint
End Get
Set(value As Point)
_DiffPoint = value
Call NotifyPropertyChanged()
End Set
End Property


赤文字のところを
Call NotifyPropertyChanged("DiffPoint")
って書くか
Call NotifyPropertyChanged(NameOf(DiffPoint))
って書くことになる
これがなんにも書かなくても良くなるから打ち間違えることもないしラク






関連記事

前回の記事 2017/6/24
WPF、CanvasLeftTopとSliderValueをBinding ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14988621.html



2016/3/1
WPFとVB.NET、Canvasの中に回転表示したコントロールのドラッグ移動で気づいたこと ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/13947862.html
こんなふうに回転させるとその上で動くマウスの位置情報も回転されてめんどくさいので
ThumbのTemplateの中に入れたCanvasを回転させているのが今回の記事




WPF、変形後の要素(Thumb)のグリッドスナップ移動

$
0
0


前回で要素の変形後の位置とサイズを取得できるようになったので、それを使って目的だった変形後のグリッドスナップ移動ができるようなった

グリッドスナップ移動
イメージ 2
橙色の四角要素をグリッド(水色罫線)に合わせて移動するとき
左上をグリッドに合わせて移動するようにしたい
要素を回転(変形)させるといろいろな左上ができる

回転後のいろいろな左上
イメージ 1
赤枠、青枠はただの目印
赤枠は回転前の位置とサイズ
青枠は回転後の要素がピッタリ収まる位置とサイズ
この2つの枠の左上が赤青矢印の先と
元の左上が回転で移動した橙色矢印の先
この3つを切り替えてスナップ移動

イメージ 3
できた!
見てて思ったのが赤枠基準はいらないかなあってのと
シングルクリックがダブルクリックになったり
マウスドッラグ中にクリックになったりしない、そんな
マウスがほしい



デザイン画面とXAML、XAMLを書くと投稿エラーになるから画像で表示するのです
イメージ 4



VBコード

Imports System.ComponentModel
Imports System.Globalization
Imports System.Windows.Controls.Primitives


Class MainWindow

Private WithEvents MyExThumb As ExThumb

'Canvasにグリッド(罫線)表示
Private Sub SetGridLine()
Dim gSize As Integer = sldGrid.Value
Dim w As Double = MyCanvas.Width
Dim h As Double = MyCanvas.Height
Dim whMax As Integer = IIf(w > h, w, h)
Dim pFigure As PathFigure
Dim pGeometry As New PathGeometry

For i As Integer = 0 To whMax / gSize
'横線
pFigure = New PathFigure With {.StartPoint = New Point(0, i * gSize)}
pFigure.Segments.Add(New LineSegment(New Point(whMax, i * gSize), True))
pGeometry.Figures.Add(pFigure)
'縦線
pFigure = New PathFigure With {.StartPoint = New Point(i * gSize, 0)}
pFigure.Segments.Add(New LineSegment(New Point(i * gSize, whMax), True))
pGeometry.Figures.Add(pFigure)
Next

With GridLine
.Data = pGeometry
.Stroke = Brushes.Cyan
.StrokeThickness = 2.0
End With
End Sub

Private Sub MyCheck()
Dim d = 119 \ 10
Dim root = MyExThumb.testRootCanvas
End Sub
Private Sub MyCheck2()
MyExThumb.SetPoint2(100, 100)
End Sub
Private Sub MyMove()
MyExThumb.MyLeft = 100
MyExThumb.MyTop = 100
End Sub

'数値確認用のTextBlockへのBinding
Private Sub SetTextBlockBinding(so As Object, sName As String, tb As TextBlock)
Dim b As New Binding(sName) With {.Source = so, .StringFormat = sName & " = {0:0.0}"}
tb.SetBinding(TextBlock.TextProperty, b)
End Sub

Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
'グリッドサイズ指定
sldGrid.Value = 30
'グリッド罫線表示
Call SetGridLine()

AddHandler btnCheck.Click, AddressOf MyCheck
AddHandler btn2.Click, AddressOf MyCheck2
AddHandler btn4.Click, AddressOf MyMove
AddHandler sldGrid.ValueChanged, AddressOf SetGridLine 'スライダーの値変更でグリッド(罫線)の表示更新

'ExThumbに100x100の赤Borderを追加してMyCanvasに表示
Dim ext As New ExThumb(New Border With {
  .Width = 100, .Height = 100, .Background = Brushes.Orange, .Opacity = 0.5})
Canvas.SetLeft(ext, 0) : Canvas.SetTop(ext, 0)
MyCanvas.Children.Add(ext)
MyExThumb = ext

'回転角度をSliderにBinding
Dim b As Binding
b = New Binding(NameOf(ExThumb.MyAngle)) With {.Source = MyExThumb, .Mode = BindingMode.TwoWay}
sldAngle.SetBinding(Slider.ValueProperty, b)

'数値確認用のTextBlockへのBinding
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyAngle), tbAngle) '角度
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyDiffPoint), tbRect) '差分座標
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyLeft), tbLeft) '実際のX座標
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyOutBounds), tbBounds) '見た目のピッタリ枠
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyTop), tbTop)
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyTransformedTopLeft), tbTTopLeft)

'目印の青枠
'ぴったり枠のRectを青枠のDataにバインディング
'値はRectからRectangleGeometryに変換する必要があるのでコンバータ使用
b = New Binding(NameOf(ExThumb.MyOutBounds)) With {.Source = MyExThumb, .Converter = New MyConverterRect}
pathRect.SetBinding(Path.DataProperty, b)

End Sub

'マウスドラッグ移動
Private Sub MyExThumb_DragDelta(sender As Object, e As DragDeltaEventArgs) Handles MyExThumb.DragDelta
Dim GridSize As Integer = sldGrid.Value
''通常移動
'MyExThumb.MyLeft += e.HorizontalChange
'MyExThumb.MyTop += e.VerticalChange

'グリッドスナップ移動
Dim x, y As Double
Dim xx, yy, xxx, yyy As Integer
Select Case True
Case rbNormal.IsChecked
'変形前の左上を基準、赤枠
With MyExThumb
x = .MyLeft + e.HorizontalChange
y = .MyTop + e.VerticalChange
xx = x \ GridSize : yy = y \ GridSize
xxx = xx * GridSize : yyy = yy * GridSize
.MyLeft = xxx : .MyTop = yyy
End With
Case rbFitFlame.IsChecked
'ぴったり枠の左上を基準、青枠(OutBounds)
With MyExThumb
x = .MyLeft + e.HorizontalChange + .MyDiffPoint.X
y = .MyTop + e.VerticalChange + .MyDiffPoint.Y
xx = x \ GridSize : yy = y \ GridSize
xxx = xx * GridSize : yyy = yy * GridSize
.SetPoint2(xxx, yyy)
End With
Case rbTopLeft.IsChecked
'変形で移動した元左上座標を基準
With MyExThumb
x = .MyLeft + e.HorizontalChange + .MyDiffPointTopLeft.X
y = .MyTop + e.VerticalChange + .MyDiffPointTopLeft.Y
xx = x \ GridSize : yy = y \ GridSize
xxx = xx * GridSize : yyy = yy * GridSize
.SetPoint3(xxx, yyy)
End With
End Select

'目印の移動
'変形で移動した元左上座標
Canvas.SetLeft(Line1, xxx)
Canvas.SetTop(Line1, yyy)
'元の枠、赤枠
Canvas.SetLeft(InBounds, MyExThumb.MyLeft)
Canvas.SetTop(InBounds, MyExThumb.MyTop)
End Sub

'ドラッグ中はマウスカーソルを手の形に
Private Sub MyExThumb_DragStarted(sender As Object, e As DragStartedEventArgs) Handles MyExThumb.DragStarted
MyExThumb.Cursor = Cursors.Hand
End Sub
'ドラッグ終了で元のマウスカーソル
Private Sub MyExThumb_DragCompleted(sender As Object, e As DragCompletedEventArgs) Handles MyExThumb.DragCompleted
MyExThumb.Cursor = Cursors.Arrow
End Sub

End Class







Public Class ExThumb

Inherits Thumb 'Thumbを継承
Implements ComponentModel.INotifyPropertyChanged '通知プロパティ用
Private RootCanvas As Canvas
Private RootRotate As RotateTransform
Public testRootCanvas As Canvas

'OutBoundsの左上座標を指定
Public Sub SetPoint2(x As Double, y As Double)
MyLeft = x + (-MyDiffPoint.X)
MyTop = y + (-MyDiffPoint.Y)
End Sub

Public Sub SetPoint3(x As Double, y As Double)
MyLeft = x + (-MyDiffPointTopLeft.X)
MyTop = y + (-MyDiffPointTopLeft.Y)
End Sub

'DiffPointとOutBoundsの更新、変形時に実行する
Private Sub SetDiffPointAndOutSize()
Dim gt As GeneralTransform = RootCanvas.TransformToVisual(Me)
Dim r As Rect = gt.TransformBounds(New Rect(New Size(RootCanvas.Width, RootCanvas.Height)))
MyDiffPoint = r.Location 'ピッタリ座標
MyOutSize = r.Size 'ぴったりサイズ
MyDiffPointTopLeft = gt.Transform(New Point) '元左上差分
Call SetOutBounds()

'未使用
Dim p1 As Point = gt.Transform(New Point) '変形前後の左上座標の差分
Dim p2 As Point = gt.Transform(New Point(RootCanvas.Width, 0)) '右上
Dim p3 As Point = gt.Transform(New Point(RootCanvas.Width, RootCanvas.Height)) '右下
Dim p4 As Point = gt.Transform(New Point(0, RootCanvas.Height)) '左下
End Sub

'OutBoundsとTransformedTopLeftの更新、移動時に実行する
Private Sub SetOutBounds()
'Dim gt As GeneralTransform = RootCanvas.TransformToVisual(Me)
Dim r As Rect = New Rect(New Point(MyDiffPoint.X + MyLeft, MyDiffPoint.Y + MyTop), MyOutSize)
MyOutBounds = r
Dim p As New Point(MyLeft, MyTop)
MyTransformedTopLeft = p + MyDiffPointTopLeft
End Sub




#Region "Property"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberName> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

'回転角度
Private Property _MyAngle As Double
Public Property MyAngle As Double
Get
Return _MyAngle
End Get
Set(value As Double)
If value <> _MyAngle Then
_MyAngle = value
RootRotate.Angle = value
Call NotifyPropertyChanged()
Call SetDiffPointAndOutSize()
End If
End Set
End Property
'X座標
Private Property _MyLeft As Double
Public Property MyLeft As Double
Get
Return _MyLeft
End Get
Set(value As Double)
If value <> _MyLeft Then
_MyLeft = value
Canvas.SetLeft(Me, value)
Call NotifyPropertyChanged()
Call SetOutBounds()
End If
End Set
End Property
'Y座標
Private Property _MyTop As Double
Public Property MyTop As Double
Get
Return _MyTop
End Get
Set(value As Double)
If value <> _MyTop Then
_MyTop = value
Canvas.SetTop(Me, value)
Call NotifyPropertyChanged()
Call SetOutBounds()
End If
End Set
End Property

'変形前後のぴったり枠の左上座標の差分
Private Property _MyDiffPoint As Point
Public Property MyDiffPoint As Point
Get
Return _MyDiffPoint
End Get
Set(value As Point)
_MyDiffPoint = value
Call NotifyPropertyChanged()
End Set
End Property
'変形後の要素がピッタリ収まるサイズ
Private Property _MyOutSize As Size
Public Property MyOutSize As Size
Get
Return _MyOutSize
End Get
Set(value As Size)
_MyOutSize = value
Call NotifyPropertyChanged()
End Set
End Property
'変形後の要素がピッタリ収まる四角枠
Private Property _MyOutBounds As Rect
Public Property MyOutBounds As Rect
Get
Return _MyOutBounds
End Get
Set(value As Rect)
_MyOutBounds = value
Call NotifyPropertyChanged()
End Set
End Property
'変形前後の元左上座標の差分
Private Property _MyDiffPointTopLeft As Point
Public Property MyDiffPointTopLeft As Point
Get
Return _MyDiffPointTopLeft
End Get
Set(value As Point)
_MyDiffPointTopLeft = value
End Set
End Property
'変形後の元左上座標
Private Property _MyTransformedTopLeft As Point
Public Property MyTransformedTopLeft As Point
Get
Return _MyTransformedTopLeft
End Get
Set(value As Point)
_MyTransformedTopLeft = value
Call NotifyPropertyChanged()
End Set
End Property

#End Region


'ControlTemplate作成、Canvasを一個入れるだけ
Private Function CreateTemplate() As ControlTemplate
Dim ct As New ControlTemplate(GetType(Thumb))
Dim c As New FrameworkElementFactory With {.Name = "RootCanvas", .Type = GetType(Canvas)}
ct.VisualTree = c
Return ct
End Function

'コンストラクタ
'渡された要素をTemplateの中のCanvasに追加する
Public Sub New(elm As FrameworkElement)
Template = CreateTemplate()
ApplyTemplate() 'Templateを再構築、必要
'TemplateのCanvasを取得して渡された要素を追加
RootCanvas = Me.Template.FindName("RootCanvas", Me)
With RootCanvas
.Children.Add(elm) 'TemplateのCanvasに追加
.Children.Add(New Label With {.Content = "左上"}) '目印用にLabelを追加
.Height = elm.Height
.Width = elm.Width
End With
testRootCanvas = RootCanvas 'test

'各種TransformをGroupにしてTemplateのCanvasのRenderTransformに指定
RootRotate = New RotateTransform
Dim sc As New ScaleTransform
Dim sk As New SkewTransform
Dim tg As New TransformGroup
With tg.Children
.Add(sc) : .Add(sk) : .Add(RootRotate)
End With
With RootCanvas
.RenderTransformOrigin = New Point(0.5, 0.5)
.RenderTransform = tg
.Background = Brushes.Transparent
End With

'ピッタリ枠とかを更新するため
Call SetDiffPointAndOutSize()
End Sub

End Class







'RectをRectangleGeometryに変換
Public Class MyConverterRect
Implements IValueConverter

Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
'Throw New NotImplementedException()
Dim r As Rect = value
Dim rg As New RectangleGeometry(r)
Return rg
End Function

Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function

End Class





前回のものに書き加えたところ
マウスドラッグ移動の部分にグリッドスナップ移動
イメージ 5
ThumbコントロールにはDragDeltaって言う便利なイベントがあって
マウスの横移動距離がe.HorizontalChangeで取得できるので、これを
今の位置に足した値を指定するだけでマウスの移動距離分移動させることができる
Canvas要素に表示しているなら
横位置の取得はCanvas.GetLeft
横位置の指定はCanvas.SetLeft
グリッドスナップしないならこれでOK、前回の記事はこれだった
''通常移動
        'MyExThumb.MyLeft += e.HorizontalChange
        'MyExThumb.MyTop += e.VerticalChange

今回のグリッドスナップは左上にあるグリッドにスナップすることにしたので
グリッドサイズが10なら10で割って小数点以下を切り捨てた値にグリッドサイズをかけた値に移動

元の横位置が115でマウスが横に1(右に1)動いた場合は
115+1=116、これを10で割って
116/10=11.6、小数点以下を切り捨てた値は
11、これにグリッドサイズの10をかけた値は
110なので110へ移動させる

マウスが横に-18(左に18)動いた場合は
115+(-18)=97
97/10=9.7
9
9*10=90
90へ移動させる

うーん、115から右に1動かした結果、左に移動することになるのは不自然だから直したほうが良さそうね

VBだと「\」って言う演算子を使うと割り算後の小数点切り捨ての値が得られる
\は\(バックスラッシュ)なんだけどフォントによって表示が円マークになる



目印用のグリッド(罫線)の表示
イメージ 6
Path要素を使って表示している、グリッドサイズをスライダーで変更した時にこれを実行して表示を更新している




変形後の要素がピッタリ収まる枠(青枠)の表示
イメージ 7
これもPath要素で表示している、PathRectがそれ
これのDataPropertyにMyOutBoundsをバインディングしている
バインディングのソースをRect、バインディングのターゲットをPath要素のDataPropertyにしている

DataPropertyに指定できるのはGeometryだけどMyOutBoundsはRectなので
RectをGeometryに変換する必要があるのでMyConverterRectっていうConverterを作成して使っている

MyConverterRect
イメージ 8
いっぱい書いてあるけど実際に書くのは青色背景の5行だけで後は自動で記入されるし、3行のところもホントは1行で済む
Return New RectangleGeometry(value)

Public Function Convertがソースからの値をターゲットへ渡す流れ
Public Function ConvertBackがその逆の流れになる
今回はConvertの方だけ使用

引数のValueにバインディングする値が入っている、ソースの値なのでMyOutBoundsのRectが入っているのでこれをRectangleGeometryに変換といっても
Rectを渡してRectangleGeometryを作成するだけ
これを返せば完了









前回のWPF記事
WPF、変形した要素を指定位置に移動、NotifyProperty ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14998511.html








スイートバジル開花、トマト(レッドオーレ)の収穫日予想

$
0
0


スイートバジル
イメージ 1
この時点で6株くらい生えていたのかな
大きいのが一株、中くらいのが3株、10センチくらいのが2株

イメージ 2
イメージ 3
前の風の影響か未だに縮れた葉っぱがでてくる

イメージ 5
この頃からあんまり美味しくなくなったのは
雨や曇りの日が続いたからか、花芽が出てきたからか
香りがかなり薄い

7/1、久しぶりに晴れた
イメージ 7
花芽も成長してきて開花間近
なんだけど
イメージ 6
奥にある小さな株が萎れている、このときはお昼くらいで
夕方に見たら
イメージ 8
さらに萎れていて
よく見たら
イメージ 9
株元がカビて枯れていた
前からかびていたんだろうけど、この日の暑さで一気に萎れたみたい
こんなふうにスイートバジルがカビるのは初めて見た

翌日
イメージ 10
この後引き抜いて処分した

スイートバジル開花、種まきから58日目
イメージ 11
1番大きな株は開花

4日での伸び具合
イメージ 12
↑4日前↓今日
イメージ 13
今は実質2株だけ


トマト(レッドオーレ)
イメージ 15
↑4日前↓今日
イメージ 14
90センチ近い

水の補給ときのこ
イメージ 16
水補給のために黒マルチをめくったところ
雨や曇りばかりで土の表面も乾燥することがないので
トマトの根がたくさん出てきている
そんな中
イメージ 17
レンガと土の境目に見慣れない白いもの

翌日、雨や曇り
イメージ 18
きのこだ!
雨や曇りの日の水補給は1~2リットル

翌日、晴れ
イメージ 19
一気に伸びていたけど折れていた
先端も枯れたようになっている
この日は結構晴れ間が多い日だったので水を3リットル追加したところ
イメージ 20
排水の穴からは水は出てこなかった
まだ入りそう

翌日7/2は朝から晴れて
イメージ 21
朝8時の時点で室温31度
夕方5時には34.4度まで上がって今年の最高室温
そして今日は15時の時点で34.6度で更新
暑い

イメージ 22
土の表面は乾燥していてた

ついでに追肥
イメージ 23
普通の化成肥料(8-8-8)がなくなったのでこれを
1週間毎に
イメージ 24
これくらいを2回入れていこうと思う

イメージ 25
表面の細かい根は枯れてしまったかなあ
昨日は3リットルでも水が溢れなかったので4リットルを入れたら

イメージ 26
排水口から溢れてきた
ってことは晴れの日は3リットルで良さそう
トマトは水を控えめにした方がいいって聞くけど
うちの環境じゃムリ
1日でも水補給を欠かすと萎れて実までシワシワになってしまう

病気になったので摘心済みの鉢植えのほう
イメージ 27
こっちは1リットルでも溢れるので
750ミリリットルくらい

株元から脇芽
イメージ 28
マルチの一部が盛り上がっているのに気づいてめくってみたら

イメージ 29
根元付近から脇芽が伸びていたw
主枝も脇芽もすべて取り除いていたら
こんなところからでも伸びてくるんだねえ、驚いた
でもこの株は伸ばすつもりはないから
イメージ 30
イメージ 31
黒マルチの下で、ほとんど日が当たらないのに
結構伸びるんだなあ

鉢植えの株が病気になった原因
イメージ 4
トマトの葉っぱにコナジラミ
プランターの株もいつ発症するのかと戦々恐々としながら見ている
見る場所は成長点付近で

イメージ 33

イメージ 32
このあたりの葉っぱ全体の形が丸くなって
葉脈以外が黄色やオレンジっぽくなっていたら発症
今のところ大丈夫だけど
今年の2株を含めて9株のうち発症しなかったのは一昨年の1株だけ


葉っぱがまるまる
イメージ 34
病気だからこんなに内側に丸まるのかなあと思ったけど
プランターの株も
イメージ 35
丸まってきた
でもよく見ると日陰になっているところは
イメージ 36
ほとんど丸まっていないので
強い日差しが原因なのかも
あと丸まるのは古い葉っぱだけで新し葉っぱは
日にあたっても丸まらない

第一花房
イメージ 38
↑一昨日、↓今日
イメージ 37
全部で9つ付いている
半分くらい摘果した方がいいのかなあと思いつつ
もったいなくてできない

一昨日の第二花房
イメージ 39
肥料が足りていれば受粉作業しなくてもほとんど結実する
ミツバチとかも来ていないからレッドオーレは自分で受粉できるみたい

初収穫日予想
イメージ 40
レッドオーレは開花からの積算温度が1000~1200度で収穫できるので
昨日今日のような暑い日(平均気温26度)が続けば7/24


6月の気温
平均気温推移
イメージ 41
今年は低め
2013年の中旬と比べると5度も低い

積算温度(平均気温の累計)
イメージ 42
今年は645度
過去4年間の平均は667.75度
(678+683+643+671)/4=667.75
と比べて22.5度低い、約1日分
645-667.5=-22.5
645/30=21.5度

1日あたりだと0.75度低い
22.5/30=0.75
確かに体感でも涼しかった
7~9月もこれくらい低くなるといいなあ


前の記事、2017/06/28
トマト去年一昨年との比較、いちごの葉っぱ大量処分、ヤクシマルリシジミの幼虫見当たらない、土の熱消毒その後 ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14997277.html



WPF、変形後の要素の4辺をグリッドスナップしながらドラッグ移動

$
0
0

前回からの続きで
今回の目的は
変形した要素がピッタリ収まる四角枠の4辺をグリッドスナップするマウスドラッグ移動


イメージ 1
青枠がぴったり枠で水色罫線がグリッド



どんな処理をすればいいのかエクセル方眼紙を使って考えてみた
エクセル方眼紙は便利なんだよなあ
イメージ 2


イメージ 3

イメージ 4

イメージ 5

イメージ 6




横移動の場合の目標グリッドの選択
両辺それぞれの左右にグリッドがあって、どちらのグリッドに合わせたらいいのかはマウスの移動方向で決める
マウスが右方向に移動していたら辺の左側を目標グリッドにする
これは、右側のグリッドはマウスの移動距離が足りなくてまだ到達していないグリッドなので目標にしても意味が無いから

目標グリッドの位置を求める
条件
左辺の初期位置が35.91、グリッドサイズが50、マウスの移動距離が50
イメージ 7
この時
初期位置+マウスの移動距離=今(移動後)の位置
35.91+50=85.91
これをグリッドサイズで割って
85.91/50=1.7182
マウスの移動距離がプラス値なら右移動、マイナス値なら左移動なので、移動距離50は右移動
右移動のときは左側を目標グリッドにする
今の位置1.7182ってことは1番目と2番めのグリッドの間にあるってことだから、左側のグリッドは1番目のグリッドってことになる
これは1.7182の小数点を切り捨てる処理をすればいい
グリッドサイズは50なので1*50=50
左辺の目標グリッド位置は50

右移動で移動後の位置がプラス値なら単純に小数点以下切り捨てればいいんだけど
マイナス値、例えば-1.5だと切り捨てると-1.0になってしまい、これだと右側になってしまう、なので別の丸め処理をする必要があって、その一覧
イメージ 8
ワークシート関数だとTRUNCとROUNDUPの2種類
VB.NETだとMath.FloorとMath.Ceilingの2種類
これで対応できた
.NETのほうにもMath.Truncateっていうのがあるんだけど
今回はFloorのほうが都合が良かった

これらは
小数点を切り捨て、切り上げ、四捨五入する: .NET Tips: C#, VB.NET
http://dobon.net/vb/dotnet/programing/round.html
こちらを参考にしました、ありがとうございます!


右辺の目標グリッドも同じように求めるので
164.09+50=214.09
214.09/50=4.2828
TRUNC(4.2828,0)=4
4*50=200
右辺の目標グリッド位置は200

それぞれの目標グリッドが得られたら、その方向と距離は有効なのかを判定
方向判定
左辺の目標グリッドは50で、左辺初期位置は35.91
初期位置から見て目標グリッドは右、マウスの移動も右なので方向は有効
右辺の目標グリッドは200で、右辺初期位置は164.09
初期位置から見て目標グリッドは右、マウスの移動も右なので方向は有効

距離判定
マウスの移動距離は50
左辺初期位置から左辺の目標グリッドまでの距離は
35.91-50=-14.09
距離は絶対値なので14.09
これよりマウスの移動距離が大きければいいので左辺移動距離は有効
右辺は
Math.Abs(164.09-200)=35.91
50>=35.91はTrueなので右辺移動距離も有効

有効判定
方向、距離のどちらも有効な場合だけその目標グリッドは有効になる
左辺、右辺の方向、距離ともに有効なので両辺ともに有効

両辺ともに有効なら近い方のグリッドを選ぶ
左辺の距離
左辺の目標グリッドー(左辺初期位置+マウスの移動距離)
=50-(35.91+50)=-35.91
=35.91
右辺の距離
=200-(164.09+50)=-14.09
=14.09
右辺のほうが近いので右辺を200の位置に移動



エクセルで確認してみる
イメージ 9

初期位置やマウスの移動距離を変更してみる
イメージ 10
エクセルは数値を変更すればすぐに確認できるから便利なんだよなあ
どんな処理や関数が必要なのかも目安が付けやすいし、処理の流れも確認しやすいと思った(小並感)

VBで書いたのがこれ、文字数節約のため画像
イメージ 11
これ(GetTargetSideLocate)をThumbのDragDeltaイベントのところから呼び出して使う

結果
イメージ 12
できた!期待どおりの動き!

これで変形後のぴったり枠の4辺のグリッドスナップはできたので
残るは去年挑戦したけどうまくできなかった変形後の4頂点のグリッドスナップ


限界突破
今回のコードは長すぎてヤフーブログの文字数上限を超えていたので
こっち
今月に入ってから暑くなって今年も限界が近づいてきたなあ
昼間に室温35度を超えるのは全く問題ないけど
深夜0時に室温33.9度は限界超えてるでしょ、暑くてうまく寝られないから睡眠不足が続いてだんだん頭が動かなくなる
平均室温33.375度はいろいろ限界だと思った



前回の記事
WPF、変形後の要素(Thumb)のグリッドスナップ移動 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15001512.html

























肥料不足のトマト(レッドオーレ)に肥料と土を追加

$
0
0

トマト(レッドオーレ)は4/28の種まきから74日目、開花から27日め
イメージ 1
↑一週間前↓昨日
イメージ 2
一週間も経ってあんまり伸びていないのは肥料不足だったみたい

イメージ 5
この二日前の7/2に追肥したんだけどねえ

イメージ 11
また台風が来るって言うから気になっていたところに支柱を追加したのと
ビニール紐で枝を固定しつつ伸ばす方向に引っ張ってみた

イメージ 12
申し訳程度の日除け要素は
バジルのマルチを張り直して余った黒ビニールより
もっと支柱があればいろいろできそうなんだけどねえ

台風後
イメージ 17
すぐ近くを通ったらしいけど
風も雨も普通の雨降り以下だった

不調な鉢植えトマトから尻腐れ果
イメージ 21
これと

イメージ 25
これも



芋虫大発生
イメージ 22
トマトの葉っぱ表

イメージ 23
葉っぱ裏

イメージ 24
たまたま見つけられたから被害はこれだけで済んで良かったわ





肥料不足?
イメージ 18
第二側枝
普通は葉柄が交互に出てくるんだけど対で出ている
これは芯止まりに近いのかも

この後成長して
イメージ 41
これを横から見ると

イメージ 42
花房の枝と葉柄が対で出ている
これも普通ならもっと離れて交互になるはず
芯止まりになならずに枝は伸びているけどまるで脇芽のように見える



イメージ 35
新し目の葉っぱの先端が枯れたようになっている
葉っぱ全体の色も紫っぽいのは肥料不足のときになっていた症状

肥料不足のときのガクのかたち
イメージ 39
第一側枝の第二花房
左が普通の形で右のようにガク同士がくっついているのは
肥料不足のときに多い気がする

イメージ 40
レッドオーレの実の形は球に近いけど
肥料不足だと縦長になる…気がする

花房の形
イメージ 49
花房が枝分かれするのも肥料不足のときに多い気がする

追肥
イメージ 26
葉っぱがスカスカだから窒素多めの肥料の油かすを試しに少し入れてみた

イメージ 27
結果的にはこれでは足りなかった感じだったので
二日後に
イメージ 32
肥料と土を追加した
ようりんはひとつかみの半分くらいと油かすは8個と
よくわからない肥料をひとつかみ


イメージ 33
肥料をばらまいて
イメージ 36

イメージ 34
土をかぶせて水を追加
水は毎日3リットルでも排水口から溢れてこないから少し足りていないかも?
これから2日経った今日だけど効果はよくわからない、様子見


第一側枝を摘心
イメージ 37
イメージ 38
ここから支柱に沿って横(画像の上)に伸ばしたいので
ちょうど横向きに出てきた脇芽を伸ばすことにしたので
この成長点は摘心した

第一果房と第二果房
イメージ 48
第一果房は大きくなるのは止まったみたいで
大きさは3~4センチ
暑い日が続いているので収穫目安の積算温度1000度超えは
7/24日以降になりそう




スイートバジル
イメージ 3
イメージ 4
花が咲いてきたので1株だけにした

イメージ 6
小さな株2つは残した大きな株から1センチ2センチくらいのところに生えていたもの
大きいのはプランターの隅っこに生えていたもの
どれも香りが薄くて美味しそうじゃなかったので廃棄した
原因は日照不足かなあ、カビが生えて枯れてしまったのもあった
日に当てると葉っぱが日焼けするからって日陰になりやすい場所においていたけど
雨や曇りの日が多すぎた

イメージ 7
悪くなさそうに見えるけど見た目だけで
香りがいまいち

イメージ 8
1株だけにしたので

イメージ 9
イメージ 10
黒マルチを全面にかけ直した

イメージ 19
雨水が入らないように真ん中を持ち上げている

イメージ 20
支柱と洗濯ばさみを結んでおいてビニールの中央を挟んで引っ張り上げている

まともな葉っぱが出てくるようになった
イメージ 28
強風の後しばらくは極端に縮れた葉っぱだったけど
普通の葉っぱが出てくるようになった

バジルも追肥
イメージ 29
こっちも油かすと土を入れてみた
バジルは葉っぱが主体だから化成肥料よりこれのほうがいいのかも

イメージ 30
油かすを配置

イメージ 31
土を追加

イメージ 45
イメージ 46
イメージ 47
今年も大きく育てるのが目的だったけど
あんまり大きくできなかったなあ
原因は間引くのが遅くて密植になったからだと思う





プラスチックの洗濯バサミ次々と壊れる
イメージ 13
約2年間紫外線に晒された結果
脆くなって簡単に折れてしまう
便利なのはわかったから多少高くても長く使えるものに変えたほうがいいかな


いちご
イメージ 14
新しく出てくる葉っぱはきれいな緑色なんだけど
一週間くらいすると黒くなって大きくならない
毎年夏はこんな感じだったかなあ

今年二度目の開花
イメージ 16

イメージ 15
一株だけ花が咲き始めた
いままで冬に開花することはあったけど夏に開花したのは初めて

イメージ 43
その後も咲終わったのは実が大きくなっているし花も新しく出てきている
このまま実るのかなあ


イメージ 44
ランナーの管理がめんどくさくなってきた
そろそろ大きいプランターに直植えしたい


前回の記事、2017/07/03
スイートバジル開花、トマト(レッドオーレ)の収穫日予想 ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15004856.html




WPF、変形後の要素(Thumb)の頂点をマウスドラッグで最寄りのグリッド頂点にスナップ

$
0
0

前回からの続きで、今回ので満足できるところまでできた

変形後の要素の4頂点をグリッド頂点にスナップ
イメージ 1
変形(20度回転)後の要素の四隅の頂点を順番にを100,100の位置にスナップ移動させている
できたなあ
一年前に失敗したのと同じ考え方なんだけど、書き方を変えてみたら期待どおりの動きにできた
今回の動画もマウスの調子が良くないから何回も撮り直した


一年前と同じ考え方
この左上頂点の場合はBが一番近い、これを左上頂点の目標グリッドとして
これを各4頂点調べるから
4*4=16通りの中で一番近いところへ移動させる




各4頂点の目標グリッドなどの情報を入れておくStructure(構造体)を



Public Structure MyTargetGridPointData
    Dim TargetGridPoint As Point '目標グリッド位置
    Dim Distance As Double '距離
    Dim IsValid As Boolean '
    Dim DiffPoint As Point '元の位置からの差分
End Structure



こんなふうに作っておいて
あとは



'指定位置から一番近いグリッド位置を返す
Private Function GetNearGridPointDistance(xy As Double, gridSize As Integer) As Double
Dim m As Double = xy Mod gridSize
If m > gridSize / 2 Then
Return xy + gridSize - m
Else
Return xy - m
End If
End Function

'指定座標から一番近いグリッド頂点の座標を返す
Private Function GetNearestGridPoint(dp As Point, gridSize As Integer) As Point
Dim x As Double = GetNearGridPointDistance(dp.X, gridSize) '最寄りのグリッドx座標
Dim y As Double = GetNearGridPointDistance(dp.Y, gridSize) 'y座標
Return New Point(x, y)
End Function
'2点間の距離を返す
Private Function GetDistance(p1 As Point, p2 As Point) As Double
Dim x As Double = p1.X - p2.X
Dim y As Double = p1.Y - p2.Y
Dim rd As Double = Math.Sqrt(x ^ 2 + y ^ 2)
Return rd
End Function

''' <summary>
''' 移動後の座標から一番近いグリッド頂点や距離、移動前からの距離などを返す
''' </summary>
''' <param name="gridSize">グリッドサイズ</param>
''' <param name="transformedPoint">移動前の座標</param>
''' <param name="mMove">マウスの移動座標</param>
''' <returns></returns>
Private Function GetPointData(gridSize As Integer, transformedPoint As Point, mMove As Point) As MyTargetGridPointData
Dim tPoint As New MyTargetGridPointData
With tPoint
'移動後地点の最寄りのグリッド頂点
.TargetGridPoint = GetNearestGridPoint(transformedPoint + mMove, gridSize)
'その距離
.Distance = GetDistance(transformedPoint + mMove, .TargetGridPoint)
'今回は未使用、値が有効かどうか
.IsValid = True
'移動前の地点から最寄りのグリッド頂点までの差分
.DiffPoint = transformedPoint - .TargetGridPoint

End With
Return tPoint
End Function

''' <summary>
''' 元の4頂点グリッドスナップ用、4頂点の最寄りのグリッド頂点を取得、一番近いグリッド頂点情報を返す
''' </summary>
''' <param name="gridSize">グリッドサイズ</param>
''' <param name="mMove">マウスの移動距離</param>
''' <param name="TopLeftXY">変形後の左上の位置</param>
''' <param name="TopRightXY">変形後の右上の位置</param>
''' <param name="BottomRightXY">変形後の右下の位置</param>
''' <param name="BottomLeftXY">変形後の左下の位置</param>
''' <returns></returns>
Private Function GetMyTargetGrid4Point2(gridSize As Integer, mMove As Point,
 TopLeftXY As Point, TopRightXY As Point, BottomRightXY As Point, BottomLeftXY As Point) As MyTargetGridPointData
'移動後の各4頂点の最寄りのグリッド頂点のData取得
Dim TLData As MyTargetGridPointData = GetPointData(gridSize, TopLeftXY, mMove)
Dim TRData As MyTargetGridPointData = GetPointData(gridSize, TopRightXY, mMove)
Dim BRData As MyTargetGridPointData = GetPointData(gridSize, BottomRightXY, mMove)
Dim BLData As MyTargetGridPointData = GetPointData(gridSize, BottomLeftXY, mMove)
'一番近いグリッド頂点を取得する
'4データを一旦リストに入れる
Dim dataList As New List(Of MyTargetGridPointData) From {TLData, TRData, BRData, BLData}

'SortedListに入れて並べ替える、距離をkeyにして昇順
Dim sl As New SortedList(Of Double, MyTargetGridPointData)
'同じ距離があった場合にリストに追加しようとするとエラーになるのでtryで無視して次のDataを入れていく
For i As Integer = 0 To 3
Try
sl.Add(dataList(i).Distance, dataList(i))
Catch ex As Exception

End Try
Next
'Dim rv As MyTargetGridPointData = sl.Values(0)
'一番近いDataを返す
Return sl.Values(0)

End Function



これをDragDeltaイベントのところで



Dim GridSize As Integer = sldGrid.Value'グリッドサイズ
Dim hChange As Double = e.HorizontalChange'マウス横移動距離
Dim vChange As Double = e.VerticalChange'マウス縦移動距離
'変形後の元の4頂点をグリッドの頂点にスナップ
'4頂点それぞれの最寄りのグリッド頂点の中から一番近いグリッド頂点やその距離の差分などを取得
Dim pData As MyTargetGridPointData = GetMyTargetGrid4Point2(GridSize, New Point(hChange, vChange), MyExThumb.MyTransformedTopLeft, MyExThumb.MyTransformedTopRight, MyExThumb.MyTransformedBottomRight, MyExThumb.MyTransformedBottomLeft)
'今と違う位置(距離の差分が0以外)ならその場所へ移動
If pData.IsValid Then
If pData.DiffPoint.X <> 0 Then MyExThumb.MyLeft -= pData.DiffPoint.X
If pData.DiffPoint.Y <> 0 Then MyExThumb.MyTop -= pData.DiffPoint.Y
End If


MyExThumbはThumbを継承して作ったクラスで
MyExThumb.MyLeftプロパティは値変更のところでCanvas.SetLeftを実行している


こんなふうに書いたらうまく行ったけど、なんかよくわかっていないんだよねえ、暑くて考えることができない

SortedListにデータ追加するところを書き換えた
一番近い距離のものを取得するのにSortedListクラスを使うのも一年前と同じなんだけど、少し書き直した
SortedListはkeyとValueを対でデータを追加していく、そうするとkeyの値の順番で自動で並び替えて登録される、なのでkeyに距離を指定すれば距離順に並ぶので簡単に一番近いものが取得できるけど、同じ値のkeyは登録しようとしてもできない(エラーになる)ので、もし別の場所で同じ距離の頂点データが出たときには、その頂点は無視して次のデータへ行くようにする処理にしたのが赤背景のところ


おまけで
変形後の要素の4辺or4頂点をグリッドスナップ
っていうのもできた

イメージ 2
変形後の要素がぴったり収まる枠(青枠)の上辺と左辺がグリッドスナップしている状態
ここから下に移動させると

イメージ 3
左下頂点がグリッド頂点にスナップ!
ここから下に移動させると

イメージ 4
右上頂点がグリッドラインにスナップ
次は

イメージ 5
青枠下辺がグリッドラインにスナップ
こんなふうに頂点や枠を近くのグリッドラインや頂点にスナップする
動きが細かすぎて使うかどうかはわからないけどこういうのもできた
この処理は前回の記事のコードの延長みたいなもので難しくないはずなんだけど今見たらよくわかんないや
暑いと頭動かない
只今(午後9時43分)の室温33.3、湿度54
でも昨日、一昨日に比べたら少しマシだなあ



今回のコード全部は
20170708_4頂点グリッドスナップ - Visual Studio Team Services
今回のでグリッドスナップの動きは全部できたかなあ、細かい不満はあるけど1年前と違って実用できる動きにできた





前回の記事
WPF、変形後の要素の4辺をグリッドスナップしながらドラッグ移動 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15011638.html



2016/05/14
一年前の記事、このときはいまいちな結果だった
WPFとVB.NET、回転したコントロールをマウスドラッグでグリッドスナップ、SortedListはスゴイヤツ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14136957.html



















プランターの株もトマト黄化葉巻病?それでも赤くなってきた、バジル終了、スベリヒユという雑草?

$
0
0

トマト(レッドオーレ)
イメージ 1
↑4日前↓今日2017/07/17
イメージ 21

プランターの株もトマト黄化葉巻病の症状が出てきた?
イメージ 5
第二側枝の成長点付近の葉っぱの色や形がそれっぽい
4日後の今日
イメージ 22
これは確定かなあ

第一側枝成長点付近
イメージ 6
こっちはまだ普通っぽいかなあ
4日後の今日
イメージ 23
それっぽくなってきた

花房の枝分かれ
イメージ 14
3つにも枝分かれするのは異常だと思う
花も小さいしガクが隣同士くっついているのもおかしい
肥料不足でもこうなるのかもしれないけど
この前追肥したばかりだからねえ

それでも液肥で追肥
イメージ 31
説明だと1000倍に薄めてってあるけど
6ミリリットルを3リットルの水で薄めた500倍を入れてきた


トマト赤くなってきた
イメージ 12
鉢植え株

イメージ 13
プランターの株も赤くなってきた
積算温度1000度超えは予想だと7/24だからだいたい合っていそう
イメージ 24
2017/07/17




スイートバジル
イメージ 2
4日前

バジル不調
イメージ 3
一部が黄色くなっている葉っぱ

イメージ 4
水不足でもないのに萎れている葉っぱ

イメージ 9
こうしてみると悪くなさそうなんだけどねえ

イメージ 10
イマイチなので後は種を採取するものだけを残した

イメージ 11
取り除いた枝、香りもいまいちなので全部廃棄
この2日後の
昨日
イメージ 16
根本が茶色くなっていた

イメージ 17
日が当たった黒マルチが熱くなって、それに触れたせいにも見えるけど
株全体が不調なのを見ると他の株でも発生していたカビかなあってことで

イメージ 18
今日でスイートバジルは終了
寒さで枯れる前に撤収するのは今回が初めて
今まで病気とかカビ生えて枯れるとかなかったんだけどねえ
原因だと思うのは、まだ初釜もないときに雨が続いたときに
雨よけのために透明ビニールシートで覆って
中が蒸れて腐ったのかなあ
2017/05/27
イメージ 33
このとき蒸れて

イメージ 34
土から腐った臭がしていた

イメージ 32
これが良くなかった気がする
この株はこの後枯れてしまった


根の状態は悪くなさそうだった
イメージ 19
根の長さは20センチ弱でこれは

イメージ 20
土の深さとぼぼ同じ
なのでスイートバジルを植える鉢の深さは20センチはあったほうが良さそう

いちご
イメージ 15
この前咲き出したいちごは実が少し大きくなったろころで
茶色くなってきた

透明ビニールシート破れる
イメージ 7
今年の4月くらいからいちごの雨よけに使っていた透明ビニールシートを流用して
準備中の土が雨に濡れないようにしていたけど風で破れてきた
といっても風が強かったわけじゃなくてビニールシートの寿命みたい
紫外線や熱で劣化して脆くなっていて少し引っ張るとパリパリ破れる
だいたい3~4ヶ月くらいだねえ、短い

今度は黒ビニールにしてみた
イメージ 8
黒ビニールのほうが厚みがある
土のマルチングでは1年くらい使っていても脆くなって破れたことはないから
こっちのほうが持つかも


雑草
イメージ 25
準備中の土に生えてきた雑草

身近な植物図鑑:スベリヒユの草姿(1)
http://sorairo-net.com/plant/08/2007/034.html
ここで探したらスベリヒユっていうらしい

やたら元気
春くらいから生えてきて夏の時期に1番元気なのがこれ
乾燥に強くて引き抜いて土の上においておいても10日くらいは枯れなくて
その間に雨が降ると普通に復活するからやっかい

ググっていたら普通に食べられるらしくて
食べられる雑草の最高峰「スベリヒユ」 - デイリーポータルZ:@nifty
http://portal.nifty.com/kiji/150821194367_4.htm
ここ見てたら今度良さそうなのが生えてきたら食べてみようかなって気になった

イメージ 26
ここまできれいに育ったのは初めて見たけど
これ食べられるんじゃないかなあって気はしていた

根は短いので簡単に引き抜ける
イメージ 28

イメージ 27
このときは食べられるって知らなかったからね


トマトの日除け
イメージ 29
スイートバジル終了であまったのを使って増設した

イメージ 30
黒ビニールと葉っぱが接触しているから葉っぱが焦げるかも



前回のベランダ菜園記事、2017/07/11
肥料不足のトマト(レッドオーレ)に肥料と土を追加 ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15018241.html








トマト(レッドオーレ)今季初収穫!(2017年)

$
0
0



トマト(レッドオーレ)
イメージ 16
4日間での変化
イメージ 3
トマト黄化葉巻病らしき症状で成長は殆ど止まっているけど
実は赤くなってきた



プランターの株もトマト黄化葉巻病イメージ 4
成長点付近の症状、葉っぱの形色、成長具合からほぼ病気確定なので
今年のトマトもここまで
あとは今ついている実を収穫したら終了予定
今年はコナジラミ類を3匹くらいしか見ていないけど
これくらいでも感染するのかしらねえ

横から見たところ
イメージ 7
左に伸ばすつもりだったんだけどねえ


今年もトマト割れる
イメージ 17
鉢植え株
あと少しで収穫できそうなんだけど割れてきた
これくらいの亀裂なら問題ないけど…
翌日
イメージ 18
亀裂が大きくなる

プランターの株も
イメージ 19
同心円状に割れるのは強い日差しで皮が硬くなったせいらしい

にわか雨
イメージ 20
気休めの日除けシートに雨が降った跡が付いていた
雨に当たるとトマトは割れる


今年のトマト初収穫
イメージ 21
収穫予定は7/24だったけど割れ方が
ひどくなってきたこの2つと

プランターの株の
イメージ 22
この2つを収穫することにした

イメージ 23
写りが良くないけど亀裂から汁が出てきている
これを収穫しないでいると腐ってくる

大きさ
イメージ 24
プランター株からは直径5センチぴったり

イメージ 25
鉢植え株も5センチ
今までは去年の5センチ弱が最大だったのを若干更新!

高さ4センチ
イメージ 26
数値的には去年と大差ないけど
イメージ 27
体感では数値以上に大きく感じる

去年の初収穫時のようす
ベランダ菜園、トマト(レッドオーレ)今季初収穫、土の熱消毒続き、イチゴのランナー ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14277279.html
毎年毎回割れるんだよねえw


ミニトマトと比較
イメージ 1
中玉トマトを実感

中身
イメージ 2
上の段がプランターの株
下段の鉢植え株のほうが少し大きい

味とか
イメージ 5
鉢植え、プランター株ともに、うちではいつものレッドオーレの味
薄味な感じ
このあと他の野菜とめんつゆと塩で煮て食べたのは美味しかったので
甘み、酸味は少ないけど旨味は多いのかも
レッドオーレは皮が硬いってのもあるから
生食より加熱調理が向いていると思った
うーん、でも育て方なのかなあ


イメージ 8
最近は晴れの日が続くので
毎日4リットル入れるようにしたけど排水口から漏れてこない
去年までは3リットルだったけど
今年の株は葉っぱが大きく育ったせいかたくさん水を消費しているみたい


この写真だけだといい感じなんだよなあ
イメージ 6
収穫数予想は30から40個

イメージ 9
大きさは3~4センチかなあ
たぶんここからはあんまり大きくならないと思う


日除け
イメージ 10
余った黒ビニールシートで気休めの日除けをしてから何日か経った
日除けになっているところの葉っぱの丸まりが抑えられている気がする
けど古い葉っぱと新しい葉っぱの差も考えられる
もう少し試してみたいけど支柱が足りない
雨よけにもなってくれたら実が割れるのも抑えられそうっていう期待もある


予想より早かった収穫
イメージ 11
7月に入ってから急に暑くなって
6月の平均気温は20~23度だったのが7月は26~29度
最初の予想では7/27だったのが7/23になっていた
実際には割れてきたので7/22に収穫

6月、7月の平均気温
イメージ 14
6月は低めで7月はいつもの年より暑いなあ
2013年も暑かったんだなあ平均気温30度!
この中からどれか選べるよって言われたら去年の2016年だけど
去年はこの後にものすごく暑くなって記事にするくらいだった

7月平均気温累計(積算温度)
イメージ 12
今年の7月はここ5年間で一番暑い!

降水量
イメージ 13
降水量は平均かな?

日照時間
イメージ 15
2017年の日照時間は5年間で最長
体感ではいつもの年と変わらなかったけどぜんぜん違うなあ
6月下旬に集中して雨や曇りでそれ以外は晴れ
以上の気象データは
気象庁 Japan Meteorological Agency
http://www.jma.go.jp/jma/index.html
を利用しました


2017年レッドオーレ
7/22の初収穫までの日数
4/28の種まきから85日
6/14の開花から38日


前回の記事(6日前)
プランターの株もトマト黄化葉巻病?それでも赤くなってきた、バジル終了、スベリヒユという雑草? ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15027899.html#15027899






2017年のベランダ菜園のトマト(レッドオーレ)まとめ

$
0
0
2017年のトマト(レッドオーレ)まとめ



有効期限が2年前の種だったけど
2つまいて2つとも発芽した

発芽から1ヶ月後にプランターと植木鉢に定植

イメージ 4
6/14に開花

いつもの病気、たぶんトマト黄化葉巻病
イメージ 5
鉢植え株も同時に開花したんだけど
新しく出てくる葉っぱに病気の症状が出始める
縮れて先端が丸くなっている、色も黄色


イメージ 6
鉢植え株は第一花房を残して芯止め
発病したら治らないので諦めた


プランター株も発病
イメージ 7
肥料不足にもなっていた
発病すると花が咲いても結実しないので
この時点で付いている実が赤くなって収穫したら終わりってことになる



収穫
7/22に初収穫
2017年は大きなものが採れた

イメージ 8
イメージ 10
病気にはなっているけど病気の影響で成長できないのは先端の部分だけで
付いている実は普通に収穫できるまでになる

割れる
イメージ 9
収穫直前になると割れるのは例年通り
原因は直射日光と雨

鉢植え株は一足先に終了
イメージ 11
イメージ 12
早くに発病していた鉢植え株は7/30で終了

イメージ 13
何かにぶつかって潰れたみたい?
原因不明


イメージ 14
だんだん割れ方がひどくなる

イメージ 17
サイズも小さくなってきた

イメージ 18
多いときは一日6個
最後の収穫日は8/22だけど
実質はこの日辺りが収穫の最後だったかなあ
トマトもそうだけど僕自身も暑くて動けなくなってきていた


100ミリくらいの雨が降った翌日
イメージ 19
割れ方がひどい

イメージ 20
これらは破棄した

最後
イメージ 21
この頃にはかなり放置気味だった
病気のせいで伸びていないので一ヶ月前とほぼ同じ姿

イメージ 22
最後の収穫、小さい

2017年トマト収穫一覧
イメージ 23
こうしてみると少なく感じる

収穫数推移
イメージ 24
前半は良かった
収穫期間は短かったけど数もそこそこで
サイズも大きなものが採れた
味は平均すればいいほうだったけど一昨年のが良かったかなあ








申し訳程度の日除け(雨よけ)要素
イメージ 15
日除けの下の葉っぱの丸まり方はゆるいけど

イメージ 16
日除けなしだとこんなに丸まる
葉っぱは差が出たけど実の方はどちらも割れていた
それでも日除けがあったほうがマシだったかなあ



後片付け
イメージ 25
最後の収穫日8/22から完全放置値して10日後

さらにその二日後
イメージ 26
この日は比較的涼しかったのかな、片付けすることに

イメージ 27
イメージ 28
兵(ただしトマト黄化葉巻病)どもが夢の跡



イメージ 2
種まき日から撤収までの日付

それぞれの期間(日)
イメージ 3
これを積み上げ横棒グラフにして
こう
イメージ 1
2017年は短かった、それでも収穫数は約64個
2015年は300個、2016年は20個
2015年は病気になっても成長が遅くなっただけで、止まらなくて12月まで収穫できたので多い
3年間で8株育てて病気にならなかったのは1株だけ
ウイルスを運んでくるコナジラミを防ぐのはムリなんだよなあ
病気なるならないは運
今年も運試ししたいけど、普通の化成肥料が底をついた、種の有効期限が3年前という条件







2016年のトマトまとめ
今年のトマトその後の顛末(まとめ) ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14583049.html

WPFでもNumericUpDownが使いたい、簡単に作りたい

$
0
0

WindowsFormアプリのNumericUpDownコントロール
イメージ 9
水色の丸のところがそれ
便利だからこんなふうにたくさん使っていたのに
WPFにはない!
ので


WPFでNumericUpDownみたいなの
イメージ 1
数値の範囲は-10から10
下側の文字やボタンは確認用

これだけならXAMLだけでできる
イメージ 2
この中の11行目から24行目

イメージ 3
StackPanelの中にTextBoxとScrollBarを入れて作っている
ScrollBarのValueを基準にすると都合がいい、最小値や最大値、変更値も決められる
ScrollBarは上ボタンを押すとValueが下がって見た目と逆になるので180度回転させている
バインディングはTextBoxのTextにScrollBarのValue
これだけでok
といっても僕が思いついたんじゃなくて
Where is the WPF Numeric UpDown control? - Stack Overflow
https://stackoverflow.com/questions/841293/where-is-the-wpf-numeric-updown-control
ここに載っていたのをそのまま


あとは少し手間を掛けて
  • TextBoxフォーカス時にテキスト全選択する
  • 数値の変更はTextBoxやScrollBarの上でマウスホイールでもできるようにする
  • 数値以外は入らないようにする
このあたりはXAMLじゃなくてC#で書いてみたのが



using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Text.RegularExpressions;

//ScrollBarとTextBoxで簡易ヌメリックUpDown
//Where is the WPF Numeric UpDown control? - Stack Overflow

namespace _20180107_NumericUpDownのようなもの2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Title = this.ToString();
Button1.Click += Button1_Click;
NumericTextBox1.MouseWheel += NumericTextBox_MouseWheel;
NumericTextBox1.GotFocus += NumericTextBox_GotFocusSelectAll;
NumericTextBox1.TextChanged += NumericTextBox_TextChanged;
NumericScroll1.MouseWheel += NumericScroll1_MouseWheel;
}


//値確認用
private void Button1_Click(object sender, RoutedEventArgs e)
{
string str;
str = NumericTextBox1.Name.ToString() + "=" + NumericTextBox1.Text.ToString() + "\n";
str += NumericScroll1.Name.ToString() + "=" + NumericScroll1.Value.ToString();
var neko = NumericTextBox1.Text;
var neko1 = NumericScroll1.Value;
MessageBox.Show(str);
}


//[WPF] TEXTBOX でフォーカス時にマウスクリックでもテキストを全選択する v2 | rksoftware
//[WPF] TextBox でフォーカス時にマウスクリックでもテキストを全選択する | rksoftware
//TextBoxフォーカス時にテキスト全選択
private void NumericTextBox_GotFocusSelectAll(object sender, RoutedEventArgs e)
{
TextBox box = (TextBox)sender;
//box.SelectAll();
this.Dispatcher.InvokeAsync(() => { Task.Delay(10); box.SelectAll(); });
}


//のぶろぐ[WPF] テキストボックスに数字以外は受け付けない簡単な方法
//正規表現で数値以外は削除using System.Text.RegularExpressions;
private void NumericTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox box = (TextBox)sender;
double d;
if (!double.TryParse(box.Text, out d))
{
box.Text = Regex.Replace(box.Text, "[^0-9-]", "");
}
}

//C#のWPFで名前からコントロールを取得する - Ararami Atudio
//TextBox上でマウスホイールを回転させた時にスクロールバーの値を上下させる
private void NumericTextBox_MouseWheel(object sender, MouseWheelEventArgs e)
{
TextBox textBox = (TextBox)sender;
Binding binding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
ScrollBar scrollBar = (ScrollBar)this.FindName(binding.ElementName);//名前から取得
if (e.Delta > 0)
{
scrollBar.Value++;
}
else
{
scrollBar.Value--;
}
}

//ScrollBar上でマウスホイールを回転させた時にScrollBarの値を上下させる
private void NumericScroll1_MouseWheel(object sender, MouseWheelEventArgs e)
{
ScrollBar sb = (ScrollBar)sender;
if (e.Delta > 0)
{
sb.Value++;
}
else
{
sb.Value--;
}
}
}
}




TextBoxフォーカス時にテキスト全選択するにはGotFocusイベント時に
SelectAllメソッドを実行すればいいけど
クリックで選択したときにも全選択したい場合はこれだとできなくて
[WPF] TEXTBOX でフォーカス時にマウスクリックでもテキストを全選択する v2 | rksoftware
https://rksoftware.wordpress.com/2016/09/06/001-48/
[WPF] TextBox でフォーカス時にマウスクリックでもテキストを全選択する | rksoftware
https://rksoftware.wordpress.com/2016/06/17/001-38/
こちらで紹介されていた方法で
イメージ 4
こう
InvokeAsyncとかTask.Delayとか初めて使った、全然理解できていない



マウスホイールでScrollBarの数値変更
イメージ 5
ScrollBarのMouseWheelイベント時にイベントのパラメーター?のeのDeltaの値を見てValueに足したり引いたりするだけ


次はTextBoxの上でマウスホイールを動かした時
イメージ 6
同じようにMouseWheelイベント時にScrollBarのValueを変更すればいいんだけど
どうやってそのScrollBarを取得すればいいのかなあってとこ

XAMLのほうでバインディングしていて対象はElementNameでしているから
TextBox textBox = (TextBox)sender;
Binding binding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
こうしてBindingOperationsのGetBindingでTextBoxのBindingを取得して
そのElementNameからScrollBarの名前までは取得できたんだけどそこからわかんなくて
FindNameメソッド、MainWindowにもあるんだねえ

名前からの取得以外だとTextBoxのTagプロパティにScrollBarを入れておくってのもいいかな



TextBoxに数値以外は入らないようにする
イメージ 7
これは全くわからなかったので即ググって
のぶろぐ[WPF] テキストボックスに数字以外は受け付けない簡単な方法
http://shen7113.blog.fc2.com/blog-entry-22.html
こちらから
これも理解できていないw
こうすると文字のキーを押しても無視されるようになる

結果
イメージ 8
できましたー



参照したところ
Stack Overflow - Where Developers Learn, Share, & Build Careers
https://stackoverflow.com/

rksoftware | C#でいろいろ調べたりしたことのメモ
https://rksoftware.wordpress.com/

Android Apps - Ararami Atudio
https://araramistudio.jimdo.com/

のぶろぐ
http://shen7113.blog.fc2.com/
ありがとうございます



至った経緯
Extended WPF Toolkit
Extended WPF Toolkit™ Community Edition - Home
http://wpftoolkit.codeplex.com/
Extended WPF Toolkitっていう便利なものの中にNumericUpDownみたいなのがあって

イメージ 10
これが使えればいいんだけど
今のバージョンだとバグがあるみたいで

イメージ 11
アプリの対象を64bitにするとデザイン画面に表示されない

イメージ 12
デバッグからリリースにしてデバッグ開始すると

イメージ 13
なにこれこわい
こんな感じなので使うのを諦めた


次にPixtack紫陽花2ndのときにはあちこち参考にして、ユーザーコントロールで作ったのがあるけど、手間がかかった割にイマイチなできに終わったのでそれも使いたくなくて、簡単に作る方法ないかなあって探していたのよねえ、そこでStack Overflowで見つかったのがScrollBarを使う方法!
今回は満足なものができたので記事にした次第


C#
今まではVisualBasicばかりだったけど、今回はC#
バブルソートの記事以来2回目かな
WPFでわからないことをググるとVBよりC#のほうが多いからC#のほうがラクかも
VBはエクセルのVBAがあるからWindowsアプリはC#にしようかなあ

Numericはヌメリック派です!
nullはヌルなんだからNumericはヌメリックなんだよなあ
でもASUSはアサス派




WPF、dpiの変更とUseLayoutRoundingを指定して画像をくっきり表示

$
0
0

BitmapSourceのCreateメソッドでBitmapSourceを作成する時にdpiを指定できるのでこれを使う
そのためには画像の色情報のbyte型配列が必要なのでこれを
BitmapImageのCopyPixelsメソッドで作成

テストに使った画像は
携帯電話で撮った写真画像
イメージ 5
72dpi、256x192のjpeg


1ピクセルずつの黒と白のストライプ画像
イメージ 6
72dpi、32x32のpng
こうしてみるとヤフーブログはdpiを無視して
画像ピクセルの大きさで表示するんだなあ


イメージ 1


画像ファイルのdpiは
ファイルの右クリックからプロパティの詳細タブで確認できる
イメージ 2
画像ファイルのdpi確認
この画像は72dpi

WindowsのDPIは標準では96dpi
僕の今の環境ではこの標準の96dpiで使っている
そこに96dpi以外の画像をWPFのImageのSourceにして表示すると
少し引き伸ばされてたり縮小されてぼやけてしまう

private void DisplayImage0()
{
//引き伸ばしされてぼやける
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg");
bitmapImage.EndInit();
MyImage1.Source = bitmapImage;
}

BitmapImageのUriSourceに画像ファイルのパスと指定したものを
ImageのSourceに指定して画像を表示
96dpiの画像ファイルならこの方法でもくっきり表示されるけど
それ以外だとぼやけてしまう


dpiを変更してくっきり表示

private void DisplayImage2()
{
var bi = new BitmapImage(new Uri(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg"));
int w = bi.PixelWidth;
int h = bi.PixelHeight;
int stride = w * 4;//決め打ち、普通のカラー画像ならこれで大丈夫なはず
byte[] pixels = new byte[h * stride];
//CopyPixelsメソッドでバイト型配列にコピー
bi.CopyPixels(pixels, stride, 0);
//配列から画像作成、この時にdpiを指定できる
var bs1 = BitmapSource.Create(w, h, 96, 96, PixelFormats.Pbgra32, null, pixels, stride);//BitmapSource
MyImage2.Source = bs1;
}

CopyPixelsメソッドとCreateメソッドを使ってdpiを指定したBitmapSourceを作成して、それをImageのSourceに指定

赤文字の96, 96ってのがdpiを指定しているところ
これが使えるのは
BitmapSourceのCreateメソッドと
WriteableBitmapのCreateメソッド
BitmapframeのCreateメソッド(これはBitmapSourceと同じ?)
他にもあるかも

これに渡す引数は
画像ファイルのパスからBitmapImageを作成して
そこからBitmapSource.Createに必要なものを作成する

画像の幅と高さはそのままPixelWidthとPixelHeightでおk

stride
これは画像の1ビクセル行の総byte数かな
カラーのjpeg画像ならだいたい32bitで読み込まれるので決め打ちしている
ファイルのプロパティのビットの深さってところが
今回の画像は24ってあるんだけどBitmapImageに読み込むと32bitになっている
32bitは4byteなのでPixelWidthに4を掛けている
これで1ピクセル行の総byte数になる

pixelsは
画像の色データを入れたもの、byte型の配列
配列の大きさは画像の高さPixelsHeightとstrideをかけたものにする

ここまでできたら
BitmapImageのCopyPixelsでpixelsの中に画像の色データをコピーする
bi.CopyPixels(pixels, stride, 0);

次に
BitmapSource.Create(w, h, 96, 96, PixelFormats.Pbgra32, null, pixels, stride);

PixelFormatsにPbgra32を指定するのは、いろいろ都合がいいから
これならパレットもいらないのでパレットにはnullを指定

これでdpiが96のBitmapSourceができるのでImageのSourceに指定して完了


Imageコントロールの方の設定で重要なのが
イメージ 3
Stretch="None"
UseLayoutRounding="True"
この2つ
とくにUseLayoutRoundingを指定しないと
イメージ 4
dpiが環境と同じでもぼやける





もう少し
8bitのグレースケール画像とかでも対応してみたのが

private void DisplayImage6()
{
FileStream fs;
WriteableBitmap wb;

//using (fs = new FileStream(@"D:\ブログ用\テスト用画像\Grayscale8bit.gif", FileMode.Open))
//using (fs = new FileStream(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg", FileMode.Open, FileAccess.Read))
//using (fs = new FileStream(@"D:\ブログ用\テスト用画像\border_column_32x32_72dpi.png", FileMode.Open, FileAccess.Read))
using (fs = new FileStream(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_8bitGrayscale_96dpi.jpg", FileMode.Open))

{
wb = new WriteableBitmap(BitmapFrame.Create(fs));
}
int w = wb.PixelWidth;
int h = wb.PixelHeight;
//strideは1pixel行のバイト数、PixelWidth * BitsPerPixel / 8
//横Pixel10の32bit画像なら、10 * 32 / 8 = 40バイト
int stride = w * wb.Format.BitsPerPixel / 8;// = 1pixel行のバイト数  Width * 32bit(4byte)
byte[] pixels = new byte[h * stride];
wb.CopyPixels(pixels, stride, 0);//コピー
var bs = BitmapSource.Create(w, h, 96, 96, wb.Format, wb.Palette, pixels, stride);
MyImage1.Source = bs;
}


FileStreamを使うのは表示している元の画像ファイルをロックしないようにするため
strideの値を求める時に、元の画像のFormat.BitsPerPixelで取得した値を8で割ったものにPixelWidthをかけるようにした、これで32bit以外の画像でも大丈夫なはず?


今回のコード全部

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;

namespace _20180109_dpiその2
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//DisplayImage6();
DisplayImage0();
DisplayImage1();
DisplayImage2();
DisplayImage3();
}
 


private void DisplayImage0()
{
//引き伸ばしされてぼやける
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg");
bitmapImage.EndInit();
MyImage1.Source = bitmapImage;
}

private void DisplayImage1()
{
//引き伸ばしされてぼやける
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(@"D:\ブログ用\テスト用画像\border_column_32x32_72dpi.png");
bitmapImage.EndInit();
MyImage3.Source = bitmapImage;
}

//dpi96に変換した画像を表示なのでくっきり
private void DisplayImage2()
{
var bi = new BitmapImage(new Uri(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg"));
int w = bi.PixelWidth;
int h = bi.PixelHeight;
int stride = w * 4;//決め打ち、普通のカラー画像ならこれで大丈夫なはず
byte[] pixels = new byte[h * stride];
//CopyPixelsメソッドでバイト型配列にコピー
bi.CopyPixels(pixels, stride, 0);
//配列から画像作成、この時にdpiを指定できる
var bs1 = BitmapSource.Create(w, h, 96, 96, PixelFormats.Pbgra32, null, pixels, stride);//BitmapSource
MyImage2.Source = bs1;
}

//dpi96に変換した画像を表示なのでくっきり
private void DisplayImage3()
{
var bi = new BitmapImage(new Uri(@"D:\ブログ用\テスト用画像\border_column_32x32_72dpi.png"));
int w = bi.PixelWidth;
int h = bi.PixelHeight;
int stride = w * 4;//決め打ち、普通のカラー画像ならこれで大丈夫なはず
byte[] pixels = new byte[h * stride];
//CopyPixelsメソッドでバイト型配列にコピー
bi.CopyPixels(pixels, stride, 0);
//配列から画像作成、この時にdpiを指定できる
var bs1 = BitmapSource.Create(w, h, 96, 96, PixelFormats.Pbgra32, null, pixels, stride);//BitmapSource
MyImage4.Source = bs1;
}


private void DisplayImage5()
{
FileStream fs;
WriteableBitmap wb;

using (fs = new FileStream(@"D:\ブログ用\テスト用画像\Grayscale8bit.gif", FileMode.Open, FileAccess.Read))
{
wb = new WriteableBitmap(BitmapFrame.Create(fs));
}
//32bitのpbgra32形式に変換
var formatConvertedBitmap = new FormatConvertedBitmap(wb, PixelFormats.Pbgra32, null, 0);
int w = formatConvertedBitmap.PixelWidth;
int h = formatConvertedBitmap.PixelHeight;
int stride = w * 4;//Width * 32bit(4byte) = 1pixel行のバイト数
byte[] pixels = new byte[h * stride];
formatConvertedBitmap.CopyPixels(pixels, stride, 0);//コピー
var bs = BitmapSource.Create(w, h, 96, 96, PixelFormats.Pbgra32, null, pixels, stride);
MyImage1.Source = bs;
}

private void DisplayImage6()
{
FileStream fs;
WriteableBitmap wb;

//using (fs = new FileStream(@"D:\ブログ用\テスト用画像\Grayscale8bit.gif", FileMode.Open))
//using (fs = new FileStream(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg", FileMode.Open, FileAccess.Read))
//using (fs = new FileStream(@"D:\ブログ用\テスト用画像\border_column_32x32_72dpi.png", FileMode.Open, FileAccess.Read))
using (fs = new FileStream(@"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_8bitGrayscale_96dpi.jpg", FileMode.Open))

{
wb = new WriteableBitmap(BitmapFrame.Create(fs));
}
int w = wb.PixelWidth;
int h = wb.PixelHeight;
//strideは1pixel行のバイト数、PixelWidth * BitsPerPixel / 8
//横Pixel10の32bit画像なら、10 * 32 / 8 = 40バイト
int stride = w * wb.Format.BitsPerPixel / 8;// = 1pixel行のバイト数  Width * 32bit(4byte)
byte[] pixels = new byte[h * stride];
wb.CopyPixels(pixels, stride, 0);//コピー
var bs = BitmapSource.Create(w, h, 96, 96, wb.Format, wb.Palette, pixels, stride);
MyImage1.Source = bs;
}

}
}





過去の関連記事
WindowsFormアプリのとき2014年
携帯電話やスマートフォンの画像も表示できるようになったPixtack紫陽花1.3.6.74 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/11521567.html

複数の画像ファイルを一度にトリミングして保存するアプリの不具合を修正した ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/11779709.html
このときはどうやって解決したのか書いてなかった

2016年
WPFとVB.NETで表示した画像をクリックした場所の色を取得はややこしい(後編) ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/13955791.html
2年前にもほとんど同じことをしていたんだなあ、成長していないw
でもこのときはUseLayoutRoundingには気づいていなかったはず

2017年
WPF、画像をくっきり表示させたい(ぼやけるのがイヤな)とき、EdgeModeとScalingMode ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14912530.html
これはdpiは関係無しでアンチエイリアスの有無とかだった


WPF、PixelFormatを変更した画像をファイルに保存、FormatConvertedBitmap

$
0
0
PixelFormatを変更してファイルに保存

イメージ 1
PixelFormatを変更している様子

コンボボックスからPixelFormatを選ぶと変更された画像に切り替わる
Saveボタンを押すとファイルに保存する
保存形式はbmp、jpeg、gif、png、tiff、wdp
ファイル名は元のファイル名+PixelFormatの名前
PixelFormatは25種類
Bgr555
Bgr565
Bgr24
Bgr32
Bgr101010
Bgra32
Pbgra32
Cmyk32
BlackWhite
Gray2
Gray4
Gray8
Gray16
Gray32Float
Indexed1
Indexed2
Indexed4
Indexed8
Rgb24
Rgb48
Rgb128Float
Rgba64
Rgba128Float
Prgba64
Prgba128Float


XAMLデザイン画面
イメージ 2


C#のコード

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;

//PixelFormats クラス(System.Windows.Media)

namespace _20180110_PixelFormatいろいろ
{
public partial class MainWindow : Window
{
private BitmapSource OriginalBitmapSource;//元画像を入れておく
private string OriginalFileName;//元画像のファイル名

public MainWindow()
{
InitializeComponent();
string filePath = "";
//filePath = @"D:\ブログ用\テスト用画像\青空とトマトの花.jpg";
filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg";

SetSource(filePath);
OriginalFileName = System.IO.Path.GetFileNameWithoutExtension(filePath);
DisplayOriginal();

ButtonSave.Click += ButtonSave_Click;
ComboBoxPixelFormats.SelectionChanged += ComboBoxPixelFormats_SelectionChanged;
AddComboBoxItem();
}

//コンボボックスの初期化、項目追加
private void AddComboBoxItem()
{
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr555);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr565);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr24);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr32);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr101010);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgra32);
ComboBoxPixelFormats.Items.Add(PixelFormats.Pbgra32);
ComboBoxPixelFormats.Items.Add(PixelFormats.Cmyk32);
ComboBoxPixelFormats.Items.Add(PixelFormats.BlackWhite);
//ComboBoxPixelFormats.Items.Add(PixelFormats.Default);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray2);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray4);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray8);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray16);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray32Float);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed1);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed2);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed4);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed8);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgb24);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgb48);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgb128Float);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgba64);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgba128Float);
ComboBoxPixelFormats.Items.Add(PixelFormats.Prgba64);
ComboBoxPixelFormats.Items.Add(PixelFormats.Prgba128Float);
}

//コンボボックスの項目変更時
private void ComboBoxPixelFormats_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
//var neko = comboBox.SelectedItem;

//PixelFormatの変換
PixelFormat pixelFormat = (PixelFormat)comboBox.SelectedItem;
var convertedBitmap = new FormatConvertedBitmap( OriginalBitmapSource, pixelFormat, OriginalBitmapSource.Palette, 0);
MyImage.Source = convertedBitmap;
TextBlockBpp.Text = "ビットの深さ(bpp) = " + pixelFormat.BitsPerPixel.ToString();
}

//保存ボタン押した時
private void ButtonSave_Click(object sender, RoutedEventArgs e)
{
SaveImageJpeg();//jpeg形式でファイルに保存
SaveImageBmp();
SaveImagePng();
SaveImageGif();
SaveImageTiff();
SaveImageWmp();
}


//画像ファイルをdpi96に変換したものを取得
private void SetSource(string filePath)
{
WriteableBitmap wb;
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
wb = new WriteableBitmap(BitmapFrame.Create(fs));
}
int w = wb.PixelWidth;
int h = wb.PixelHeight;
int stride = wb.BackBufferStride;
byte[] pixels = new byte[h * stride];
wb.CopyPixels(pixels, stride, 0);
var bs = BitmapSource.Create(w, h, 96, 96, wb.Format, wb.Palette, pixels, stride);
OriginalBitmapSource = bs;
TextBlockPixelFormat.Text = " = " + bs.Format.ToString();
}

private void DisplayOriginal()
{
MyImage.Source = OriginalBitmapSource;
}


//jpeg形式でファイルに保存
private void SaveImageJpeg()
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
//MessageBox.Show(encoder.QualityLevel.ToString());//75
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".jpg";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageBmp()
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".bmp";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImagePng()
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".png";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageGif()
{
var encoder = new GifBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".gif";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageTiff()
{
var encoder = new TiffBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".tiff";
var neko = encoder.Compression;
//encoder.Compression = TiffCompressOption.Ccitt3;
using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageWmp()
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".wdp";
var neko = encoder.CodecInfo.FileExtensions;
using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private string GetPixelFormatName()
{
BitmapSource bitmapSource = (BitmapSource)MyImage.Source;
return bitmapSource.Format.ToString();
}
}
}


ファイルパスから画像取得
イメージ 3
前回と同じようにdpiを96に変換している
作成したBitmapSourceはあとでPixelFormatを変更するので
どこからでも参照できるフィールド変数?に入れておく
イメージ 4
ここのOriginalBitmapSource


コンボボックスの初期化、項目を追加する
イメージ 5
ComboBoxのItemsにPixelFormatをそのまま入れている
一個一個入れるんじゃなくて一括でできればいいんだけど、方法がわからん
でも、文字と数値しか入れられないイメージだったけど入るんだねえ、便利


コンボボックスの項目変更時にPixelFormatを変更
イメージ 6
変更に使うのはFormatConvertedBitmapって言うクラス
これをNewで作る時
FormatConvertedBitmap(BitmapSource, PixelFormat, BitmapPalette, Double)
引数のBitmapSourceとBitmapPaletteは元画像のものを渡して、
PixelFormatはComboBoxのSelectedItem、
最後のdoubleは透明度に関係あるっぽい、0を渡しとけばいいみたい
これでできたものをImageコントロールのSourceに指定すると表示される


ファイルとして保存
ファイル名にPixelFormatの名前を付けたかったので
PixelFormatの名前はToStringで取得
イメージ 8

jpeg形式で保存の場合
イメージ 7

bmp形式の場合
イメージ 9
他の形式も同じように細かい設定は何もしないで保存している

PixelFormatの中には128bitとかもあるのに、これでできているのかなあと疑問に思うので保存されたファイルを見てみると

PixelFormatがPrgba128Floatをbmp形式で保存したファイルのプロパティ
イメージ 10
ビットの深さは64になっている
ずいぶん減っているけど64bitのbmpって初めてみた
普通だと24とか32


次はgif画像
イメージ 11
8bit
gifは最大で8bitみたい


jpeg
イメージ 12
24bit
これもjpeg形式の最大値だと思う


png
イメージ 13
おお、64bit、これも初めてみた


tiff
イメージ 14
これも64
tiff画像は馴染みがないから、よくわからん


最後にwdp
イメージ 15
128!

エクスプローラーから見る限りでは各画像形式の上限らしきbitで保存できているみたい
元の画像が24bitだしモニタも普通の液晶ディスプレイだから以上になってもきれいにはならないけどね



次は1bit
イメージ 16
白か黒の2色のPixelFormatはBlackWhite
単純な2値化じゃなくて誤差拡散法のディザリングみたい

結果
形式bit
bmp1
gif8
jpeg8
png1
tiff1
wdp1
gifとjpegが8bitになっている、jpegはなんとなく仕方がないかなあって気もするけどgifはどうなんだろう、違う気がする
その他は同じ1bitで保存されている、けど
wdpだけうまく保存できていなくて

イメージ 17
これはEdgeで開いたところ、うまく保存できていない
ファイルサイズもbmp形式で6KBなのにwdpは0.3KBと極端に小さくなっている
PixelFormat.BlackWhiteをWmpBitmapEncoderで
正しく保存するにはコーデックの設定を変更する必要があるのかも

wdpって今回初めて見たんだけど
HD Photo - Wikipedia
https://ja.wikipedia.org/wiki/HD_Photo
JPEG XR - Wikipedia
https://ja.wikipedia.org/wiki/JPEG_XR
マイクロソフトが作ったもので
今ではJEPG XRって言うみたい?
拡張子もwdpじゃなくてjxrのほうが良かったかなあ

この画像形式を開くことができるアプリは
Windowsのフォト
Windows フォトビューワー
インターネットブラウザのEdge
エクスプローラーで表示形式を大きなアイコンとかにする
などWindows標準のアプリなら見ることができた

WindowsFormアプリのPixtack紫陽花は見られなかったけど
WPFアプリのPixtack紫陽花2ndなら見ることができた!


保存されたファイルのサイズ一覧
イメージ 32
コーデックの設定は全く変更していないので
jpegとwdpは参考程度
jpegのQualityは75だった


保存された画像ファイルのbpp一覧
イメージ 24
wdpはこの中では一番新しい形式なだけあって128bitにも対応している
気になるのがCmyk32をjpegで保存したファイル、32bitになっている

イメージ 18
jpegで32bitなんてあるのかな

エクスプローラーで見ると
イメージ 20
普通に表示されている
これを

ヤフーブログもjpegファイルは普通に投稿できるので
このファイルを載っけてみると
イメージ 19
あれ、色がぜんぜん違う!

google Chromeで表示
イメージ 21
妙に鮮やかになっている

ペイント
イメージ 22
正常、ほとんどのアプリは普通に表示された
でも色が変わるアプリもあるから
Cmyk32をjpeg形式で保存したファイルは、なんかおかしいんだろうねえ

色相を180度移動で反対の色にしてみたら
イメージ 23
ヤフーブログで表示される色に似ているねえ



イメージ 25
元が24bitだからそれ以上のフォーマットは意味ないけど一覧
面白いのは減色処理みたいになる、24未満になるものかな
Indexed1から8までの4つはパレットを持った画像になるみたい

Indexed1は1ビットなので2色
イメージ 26
この2色は
イメージ 27
これで
元の画像から選定してパレットに登録して使っているのかな
この色の選び方がスゴイと思う

他の画像で
イメージ 35
この画像のPixelFormatをIndexed1にすると
イメージ 36
違う2色になる

Indexed2は2ビットなので4色
イメージ 28
これがたった4色で表現されている

イメージ 29
この4色
どうやって選んでいるのかなあ

別の画像でIndexed2
イメージ 37
色は違うけどこれも4色
そういえば昔のプリンターは4色が普通だったなあ


Indexed4は4ビットなので16色
イメージ 30

Indexed8は8ビットなので256色
イメージ 31


gifファイルのハッシュ値一覧
イメージ 33
gifはすべて8bitのファイルが出来上がったけど中身は違っているのか気になった
とくにIndexed8は8bitだからgifの最大色数256色と同じだからどうかなってみたら
アルファチャンネルを含まないRGB系と全く同じ
パレットはどんな扱いになっているんだろうねえ

bmp
イメージ 34
Rgb101010はファイルサイズもファイルのbppも64bitや128bitのファイルと同じなのに中身は違うみたい
ファイルヘッダのbppを記録する部分が違うだけだったりして


追記
さっきの色が変わった画像を記事を投稿してからダウンロードしてみた
イメージ 38
ビットの深さ24になっている
ヤフーブログに32bitのjpeg画像をアップロードすると
その時点で24bitに変換されるってことかなあ
32bitのjpeg画像はありえないってことかしらw



WPF、前回の64bppとかの画像ファイルを読み込んでPixelFormatを確認してみた

$
0
0


前回は画像のPixelFormatをいろいろ変更してファイルに保存した
正しく保存されていないのもあったけど、それは置いといて
今回はこのファイルを、いくつかの方法で読み込んで
PixelFormatやbpp(ビットの深さ)を確認してみた

イメージ 1
この画像ファイルのビットの深さは64のpngファイル


ビットの深さ(色深度)
色の深さ64なら64bppとか64bitとかもたぶん同じ意味
イメージ 2
 ファイルのプロパティ
このファイルを読み込んでBitmapSourceにしてみる


4つの方法で試した

//方法1
//BitmapImageから作成
private BitmapSource Test(string filePath)
{
return new BitmapImage(new Uri(filePath));
}

//方法2
//BitmapFrameから作成
private BitmapSource Test2(string filePath)
{
return BitmapFrame.Create(new Uri(filePath));
}

//方法3
//BitmapDecoderから作成
private BitmapSource Test3(string filePath)
{
BitmapDecoder decoder = 
BitmapDecoder.Create(
new Uri(filePath),
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.OnLoad);
return decoder.Frames[0];
}

//方法4
//FileStreamとBitmapDecoderから作成
private BitmapSource Test4(string filePath)
{
BitmapDecoder decoder;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
decoder = BitmapDecoder.Create(
fs, 
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.OnLoad);
}
return decoder.Frames[0];
}

2018/01/14修正
方法3と方法4の赤文字のところ
BitmapCacheOption.Default);を
BitmapCacheOption.OnLoad);に
修正

期待するのはPixelFormatはRgba64でビットの深さは64bppの
BitmapSourceが取得できること

結果
方法1から3だと
イメージ 3
PixelFormatはPbgra32でbppは32になっている、違う、そうじゃない


期待どおりだったのは方法4だけだった
イメージ 4
PixelFormatはRgba64でbpp(BitsPerPixel)が64で期待どおり!


方法4とそれ以外の違いはFileStream使用の有無
方法2のBitmapFrameはFileStreamからも作成できるので、これを書き換えて

方法2’
//BitmapFrameから作成、FileStreamを使う方法
private BitmapSource Test2(string filePath)
{
//return BitmapFrame.Create(new Uri(filePath));

BitmapFrame frame;
using (FileStream fs = 
new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
frame = BitmapFrame.Create(fs, 
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.OnLoad);
}
return frame;
}

2018/01/14修正
赤文字のところ
BitmapCacheOption.Default);を
BitmapCacheOption.OnLoad);に
修正

こうしてみたらこれでも正しく読み込めた!
なのでファイルのフォーマットどおりに読み込みたいときは、FileStreamを使えばいいみたい
方法4と方法2’を比べるとラクそうなのは方法2’かな

FileStreamがいまいちよくわかっていないのと
BitmapFrame.Createの引数のBitmapCreateOptionsとBitmapCacheOptionもぜんぜんわからん


前回の
24bppのjpegファイルをBgr32のBitmapSourceとして読み込んで
PixelFormatをいろいろ変更して画像ファイルとして保存
ファイルのプロパティに表示されるビットの深さ、bpp一覧
イメージ 5

↑のこれらを方法1から3で読み込んだ結果一覧が
イメージ 21
一致しないのが多けど
普通の画像なら32bpp以下だからこれでも問題ないんだよねえ
Cmyk32ってのがよくわかんない
4色でそれぞれ8bitだから合計32bitならいいのかなあ

次は
方法4で読み込んだ結果一覧
イメージ 22
かなり改善された!
とくにwdp形式はかなり正確になっている
bmpのdefaultってのは説明を読んでもわからなかったけど64bppだった

追記2018/01/14
BitmapCreateOptions.PreservePixelFormat
っていうのを使ったから正確になったみたいで
【C#】画像ファイルのカラーモードを判断する方法 - Sweets Junkie
http://sweets-junkie.hatenablog.com/entry/2014/03/12/053024
こちらが参考になりました、ありがとうございます

BitmapCreateOptions.PreservePixelFormatを指定しないと
自動で最適なPixelFormatに変更されてしまうみたい
確かに今の環境では32bpp以上あってもほとんど無意味だからねえ
追記ここまで


bppとパレットの色数一覧
イメージ 6



WPFのPixelFormats
PixelFormats クラス (System.Windows.Media)
https://msdn.microsoft.com/ja-jp/library/system.windows.media.pixelformats(v=vs.110).aspx





確認のために作ったアプリ
方法1から3で確認
イメージ 7
Rgb128FloatなのにBgr32で取得されている

方法4と方法2’
イメージ 8
正確に取得できている



いろいろなbpp(ビットの深さ)のpngを投稿してみる

1bpp
イメージ 9
BlackWhite.png

イメージ 13
Indexed1.png


2bpp
イメージ 10
Gray2.png

イメージ 14
Indexed2.png


4bpp
イメージ 11
Gray4.png

イメージ 15
Indexed4.png


8bpp
イメージ 12
Gray8.png

イメージ 16
Indexed8.png


32bpp
イメージ 17
Bgra32.png
これが一番普通だと思います


48bpp
イメージ 18
Rgb48.png


64bpp
イメージ 19
Prgba64.png

イメージ 20
Rgba64.png

前回は32bppのjpegを投稿したら変な色になったけど
64bppや48bppのpngは普通に表示されるんだなあ
これらを名前を付けて保存したらどうなるのかな


追記
2018/01/14修正した箇所の
frame = BitmapFrame.Create(fs, 
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.OnLoad);
これね、赤文字のところ
修正前はDefaultにしていた、もう一度試していたらエラーが出るようになっていたので、色々いじってOnLoadにしたらエラーにならず画像も表示された
けどなんでこれいいのか全然わかっていない

BitmapCacheOption 列挙型 (System.Windows.Media.Imaging)
より引用
Default
イメージ全体をメモリにキャッシュします。 これが既定値です。

None
メモリ保存は作成しないでください。 イメージのすべての要求はイメージ ファイルから直接入力されます。

OnDemand
要求されたデータに対してのみ、メモリ保存を作成します。 最初の要求で、イメージが直接読み込まれます。後続の要求はキャッシュから入力されます。

OnLoad
読み込み時にイメージ全体をメモリにキャッシュします。 イメージ データのすべての要求はメモリ保存から入力されます。
引用ここまで
説明読んでも理解できないゾw
2018/01/14追記ここまで



64bpp pngとかでググっていたら
とある無職の悪戦記録: C#で64bitPNG
http://hima-tubusi.blogspot.jp/2017/09/c64bitpng.html
64bitのpngを作成している方がいらっしゃいました!すごい!



前回の記事
WPF、PixelFormatを変更した画像をファイルに保存、FormatConvertedBitmap ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15320177.html



ベランダ菜園の様子、去年(2017年)秋に植えてから今日までのニンニクの様子といろいろ遅れた原因

$
0
0

今日のニンニクの様子
イメージ 1
今回はプランターを増やして2つ!


植えた時の様子
イメージ 2
春に収穫したものを残しておいたもの
右上の塊は収穫中で一番大きかったもの
それ以外は別々の株から大きそうなものを選んでおいたのと
ケースに入っているのはむかごとにんにくの芽がでなかった小さなもの

塊をバラにした
イメージ 4
10個の鱗片から成っていた
品種は遠州極早生だと思われるもの
皮が紫色なんだけど乾燥すると茶色くなる

殻(皮)を破って根が出てきている
イメージ 3
日付がね、もう11月だからね
本当は10月上旬に植えたかったけど1ヶ月も遅れたけど
前回の1年遅れよりはマシ

前回は
こんな状態のを植えた


位置を決める
イメージ 5
こんな感じかな
大きくなってもいいように前回より間隔を広げた
プランターA


イメージ 6
だいたい7~8センチ間隔
このプランターは15個


もう1つのプランターBは小さめのにんにく
イメージ 7
数えると26か27個かな

イメージ 8
6センチ間隔と

イメージ 9
むかごは4センチ間隔

イメージ 10
場所が決まったら指でギューって押し込んで

イメージ 11
深さ2~3センチ

土をかけて完了
イメージ 12
左がプランターA、右がB
土は乾燥していなかったから水も掛けていない
前回に遅れること約2週間、どうなるかなあ

前回
一つのプランターに40個以上植えていた


植えてから6日後
イメージ 13
この間に雨が降った

プランターB
イメージ 14
芽が出ていた!

イメージ 15
水色矢印は芽が出たところなんだけど
ピンクのところは違うのが出てきている

イメージ 16
ニンニクそのものが生えてきた

イメージ 17
イメージ 18
どういうことなの?


イメージ 19
埋め直すために一旦掘り出した

イメージ 20
根が下じゃなくて横に伸びている
ニンニクの下に障害があって根を下に伸ばすことができないから
作用反作用みたいな感じでニンニク自体が上に押し出されたのかも

埋め直した
イメージ 23
少しだけ頭が出ているのは様子見

原因?
イメージ 21
植える直前のプランターBの様子
雑草が生えていて、これを抜き取らずに土に混ぜたところに植えたから
これが団子状の塊になったのがちょうどニンニクの下になったのかも
次からは植える直前の雑草は土に混ぜないようにしよう


プランターA
イメージ 22
こっちは雑草がほとんどなかったせいか
ニンニク自体が生えてくることなく、芽だけが出てきた


それから5日後
イメージ 24

イメージ 25
プランターA
おお、だいぶ発芽数が増えて
15個全部が発芽した一方

プランターB
イメージ 26
出てきてはいけないものが…

イメージ 27
出てきてしまった
これは場所的に一旦埋め戻したものだなあ



      _,∠⌒ヽ、 
     l|::|   ̄ `l 
     ||]| `・ω・´|  シャキーン !! 
     |l::|  ,.、  | 
     ||]| <YEC>|   i 
   i!   |l;;|   `'´ j  !l 
 i | !  );;)二 ニ(   ! 
 | |i l (;;{_    _)    ! 
   !| | i   ミ三彡   i |! ! 
    i |!   ミ三彡  l| l|!| |i 
  l l| i!  ,ミ三彡  |! i| ! ! 
  i!    に,二,二l  ! j 
       ∥∥ 
         ` ∥ 
           ` 
このコンデンサのAA思い出す


イメージ 28
根は下に伸びているから問題なさそうなんだけど
なんで出てくるのかなあ
元の世界にお帰り…


今回の植え付けが遅れた原因
イメージ 29
めんどくさい状況が発覚したのが10月
解決するために外に出た時撮った写真
自転車ないから徒歩だよ、寄り道して10kmだったかな
外に出るとか引きこもりの風上にも置けない所業

イメージ 30
11月も終わりで結構寒いのに
キャベツ畑ではモンシロチョウが飛んでいた
写真中央左に2匹写っているのは…見えないね
途中では4~5匹見た、寒くないのかなあ
でもこれでめんどくさいのが終わったのでいろいろ動けるようになった
ブログも10月には再開する予定だったけど遅れた

12月
イメージ 31
植えてから約1ヶ月
ほとんど発芽した

イメージ 32
プランターA

イメージ 33
プランターB
埋め戻したものも無事発芽


イメージ 34
10~15センチ
この成長具合はどうなんだろうと前回と比べてみると

前回の同じ時期
前回は暖かくした方がいいかなと思って
ビニールで覆っていた
比べると、そんなに違いはない…かな?
今回は2週間も遅れたけど球根の状態が良かったからかなあ


肥料
イメージ 35
肥料を全く入れていなかったので
ここに来てやっと追肥
でも普通の化成肥料はなくなって残っているのがこの
よくわからない肥料をひとつかみ追肥した

長芋?
イメージ 36
完全に放置されている土野雑草を整理するついでに
掘り返してたら長芋?山芋?が出てきた

イメージ 37
こんな小さいけど2年か3年植わっていたと思う
毎年同じようなところからツルが伸びていた
味は普通だった

いちご
イメージ 38
夏は例年通り放置していた
秋に植え替えする予定だったけど
先の理由で流れてそのままになった


12月下旬
イメージ 39
にんにく斜めになる
葉先が黄色くなる
なんで?

アブラムシ
イメージ 40
前回の冬にはいちごの葉っぱにたくさんのアブラムシが居たけど
今回の冬は少ない、ほとんど居ない
植え替えをしなかったから?
肥料を入れていないから?

今年(2018年)1月
イメージ 41
斜め化と葉っぱの黄化が進む

イメージ 42
大きなのは26センチ
むかごからのは10センチ以下かな
でもこんなに寒いのによく成長するなあ

前回の同じ時期
これは26センチ以上あるねえ
やっぱり植え付け時期とビニールで覆った環境の差がありそう



今日2018/01/14
イメージ 43
斜め

イメージ 44
ひどいのは45度くらい傾いている
風の向きとは違うから関係なさそうだし
株によって向きが違うのもあるし、なんだろう



寒くて撮影できない
充分に充電されている携帯電話で撮影していても
撮影から2分くらいすると電池がなくなったから充電してくださいって
表示が出て電源が落ちるようになって
ぐぐってみたらバッテリーって寒いと力が出せないのね
その後充電しなくても温めてあげたら普通に起動した




関連記事
2016年植え付けの様子
ベランダ菜園、1年半前に収穫したにんにくを植えた結果 ( ガーデニング ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14593610.html





WPF、画像ファイルを開く方法まとめ

$
0
0

Imageコントロールに表示するBitmapSourceを画像ファイルから作成するときに
  1. dpiを指定するか画像ファイルに合わせるか選択したい
  2. PixelFormatを画像ファイルに正確に合わせるか環境に最適なものに変更するか選択したい
  3. PixelFormatを指定したものに変更したい
個人的にこの3つの希望があって
1と2を合わせたのがこれで

/// <summary>
/// ファイルパスとPixelFormatを正確にするか最適にするかを指定してBitmapSourceを取得、
/// dpiの変更は任意
/// </summary>
/// <param name="filePath">画像ファイルのフルパス</param>
/// <param name="accuratePixelFormat">Trueなら画像ファイルと同じ正確なPixelFormat、
/// Falseは今の環境で最適なものに変更される</param>
/// <param name="dpiX">無指定なら画像ファイルで指定されているdpiになる</param>
/// <param name="dpiY">無指定なら画像ファイルで指定されているdpiになる</param>
/// <returns></returns>
private BitmapSource GetBitmapSource10(
string filePath, bool accuratePixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
BitmapFrame bitmapFrame;
//PixelFormatを元の画像と同じにするかパソコンの環境に合わせるか
if (accuratePixelFormat)
{//画像と同じ
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
}
else
{//環境に最適なものに変更
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.None,
BitmapCacheOption.Default);
}
int w = bitmapFrame.PixelWidth;
int h = bitmapFrame.PixelHeight;
int stride = (w * bitmapFrame.Format.BitsPerPixel) / 8;
byte[] pixels = new byte[w * stride];
bitmapFrame.CopyPixels(pixels, stride, 0);
//dpi指定がなければ元の画像と同じdpiにする
if (dpiX == 0) { dpiX = bitmapFrame.DpiX; }
if (dpiY == 0) { dpiY = bitmapFrame.DpiY; }
//dpiを指定してBitmapSource作成
source = BitmapSource.Create(
w, h, dpiX, dpiY,
bitmapFrame.Format,
bitmapFrame.Palette, pixels, stride);
};
return source;
}

これを使って画像ファイルを開いてみる
イメージ 2
いつものテスト用画像
画像サイズ256x192
ビットの深さ24bit(24bpp)
dot per inch72dpi
画像形式jpeg
のごく普通の写真画像

GetBitmapSource10(filePath, false);
2番めの引数accuratePixelFormatはFalseを指定、dpiは無指定で開くと
イメージ 1
左上にある文字列は確認用

画像ファイルから取得した72dpiで表示されている
WindowsのDPIは初期設定の96なので72dpiだと多少拡大されるのでぼやける
PixelFormatは元のBgr24から最適なものに変更されてBgr32になっている
今のWPF的にはBgr24はBgr32にするのが最適?らしい、よくわからん


最適化なんか要らない!Bgr24はBgr24で読み込んでほしいときは
GetBitmapSource10(filePath, true);
accuratePixelFormatをTrueで開くと
イメージ 3
最適化されず元のPixelFormatと同じBgr24で取得できる



dpiの指定
GetBitmapSource10(filePath, true, 96, 96);
96指定で開くと
イメージ 4
くっきり表示
96dpiで表示されているのが左上でも確認できる











PixelFormatの指定(変更)して取得

1と3を合わせたのがこれ

/// <summary>
///  ファイルパスとPixelFormatを指定してBitmapSourceを取得、dpiの変更は任意
/// </summary>
/// <param name="filePath">画像ファイルのフルパス</param>
/// <param name="pixelFormat">PixelFormatsの中からどれかを指定</param>
/// <param name="dpiX">無指定なら画像ファイルで指定されているdpiになる</param>
/// <param name="dpiY">無指定なら画像ファイルで指定されているdpiになる</param>
/// <returns></returns>
private BitmapSource GetBitmapSourceWithCangePixelFormat2(
string filePath, PixelFormat pixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
var bf = BitmapFrame.Create(fs);
var convertedBitmap = new FormatConvertedBitmap(bf, pixelFormat, null, 0);
int w = convertedBitmap.PixelWidth;
int h = convertedBitmap.PixelHeight;
int stride = (w * pixelFormat.BitsPerPixel) / 8;
byte[] pixels = new byte[w * stride];
convertedBitmap.CopyPixels(pixels, stride, 0);
//dpi指定がなければ元の画像と同じdpiにする
if (dpiX == 0) { dpiX = bf.DpiX; }
if (dpiY == 0) { dpiY = bf.DpiY; }
//dpiを指定してBitmapSource作成
source = BitmapSource.Create(
w, h, dpiX, dpiY,
convertedBitmap.Format,
convertedBitmap.Palette, pixels, stride);
};
return source;
}

これを使って
GetBitmapSourceWithCangePixelFormat2(filePath, PixelFormats.Indexed2, 96, 96);
PixelFormatをIndexed2を指定、dpiは96を指定している
イメージ 6
PixelFormatがIndexed2に変更されて表示される
Indexed2は4色の2bit画像



今度はこの画像でテスト
イメージ 5
画像サイズ1024x768
ビットの深さ24bit(24bpp)
dot per inch72dpi
画像形式jpeg
さっきの画像と違うのは画像サイズだけ
これを

GetBitmapSourceWithCangePixelFormat2(
filePath, PixelFormats.Indexed2, 300, 300);

PixelFormatはIndexed2,dpiを300に指定
イメージ 7
Indexed2で4色に減色と
dpiが大きくなったので小さく表示される
モアレが出ているかなあ、96の倍数なら?

300/96=3.125
96*3=288
300/72=4.166666666666
72*4=288
たまたま288は96と72の公倍数だったので
イメージ 8
あんまり変わんないかなw
でもファイルサイズはさっきの半分以下になった


72*3=216dpi指定
イメージ 9
いまいち


96*2=192指定
イメージ 10
いいね!





その他コード

/// <summary>
/// 画像のファイルパスから画像のdpiXを取得
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private double GetDpiX(string filePath)
{
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
return BitmapFrame.Create(fs).DpiX;
}
}

/// <summary>
/// 画像のファイルパスから画像のPixelFormatを取得
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private PixelFormat GetPixelFormat(string filePath)
{
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
return BitmapFrame.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default).Format;
}
}




画像ファイルを開く

//かんたんなのはこれ、ただし
//元のファイルはロックされるので移動や名前変更できない
//PixelFormatはPC環境?に最適なものに変更される
//dpiは画像ファイルに準拠するので、表示する環境のdpiと違う場合は画像がぼやける(Windowsの標準のDPIは96)
//以上はすべて一長一短
private BitmapImage GetBitmapImage(string filePath)
{
return new BitmapImage(new Uri(filePath));
}


//dpiを指定しない(画像で指定されているそのまま)
//PixelFormatは元の画像から正確に取得
private BitmapSource GetBitmapSource1(string filePath)
{
BitmapFrame source = null;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
source = BitmapFrame.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
};
return source;
}

//dpiを指定
//PixelFormatは元の画像から正確に取得
private BitmapSource GetBitmapSource2(string filePath, double dpiX, double dpiY)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
//以下の3つのBitmapCacheOptionどれでも取得できる、どう違うのかがわからん
var wb = new WriteableBitmap(BitmapFrame.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default));
//var wb = new WriteableBitmap(BitmapFrame.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None));
//var wb = new WriteableBitmap(BitmapFrame.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad));
int w = wb.PixelWidth;
int h = wb.PixelHeight;
int stride = wb.BackBufferStride;//WriteableBitmapを使うのはこれがラクに取得できるから
byte[] pixels = new byte[w * stride];
wb.CopyPixels(pixels, stride, 0);
//dpiを指定している
source = BitmapSource.Create(w, h, dpiX, dpiY, wb.Format, wb.Palette, pixels, stride);
};
return source;
}

//dpiを指定
//PixelFormatは最適に変更
private BitmapSource GetBitmapSource3(string filePath, double dpiX, double dpiY)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
var wb = new WriteableBitmap(BitmapFrame.Create(fs, BitmapCreateOptions.None, BitmapCacheOption.Default));
int w = wb.PixelWidth;
int h = wb.PixelHeight;
int stride = wb.BackBufferStride;//WriteableBitmapを使うのはこれがラクに取得できるから
byte[] pixels = new byte[w * stride];
wb.CopyPixels(pixels, stride, 0);
//dpiを指定している
source = BitmapSource.Create(w, h, dpiX, dpiY, wb.Format, wb.Palette, pixels, stride);
};
return source;
}



PixelFormatだけ変更して開きたい時

//dpiは指定しない
//PixelFormatは指定のものに変更
private BitmapSource GetBitmapSourceWithCangePixelFormat1(string filePath, PixelFormat pixelFormat)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
var bf = BitmapFrame.Create(fs);
var convertedBitmap = new FormatConvertedBitmap(bf, pixelFormat, null, 0);
source = convertedBitmap;
};
return source;
}




関連記事
WPF、dpiの変更とUseLayoutRoundingを指定して画像をくっきり表示 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15316739.html
WPF、PixelFormatを変更した画像をファイルに保存、FormatConvertedBitmap ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15320177.html
WPF、前回の64bppとかの画像ファイルを読み込んでPixelFormatを確認してみた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15322703.html



WPF、Tiff画像の圧縮形式とファイルサイズ、TiffBitmapEncoderのCompress

$
0
0


今までは何も指定しないで作成していたけど圧縮(エンコード)方式をいくつか選べるみたいなので試してみた
TiffBitmapEncoder.Compression プロパティに
TiffCompressOption列挙型から選んで指定する


TiffCompressOption 列挙型 (System.Windows.Media.Imaging)
から引用
Ccitt3
CCITT3 圧縮スキーマが使用されます。

Ccitt4
CCITT4 圧縮スキーマが使用されます。

Default
TiffBitmapEncoder エンコーダーは、実行できる最適な圧縮スキーマで、ビットマップを保存しようとしています。

Lzw
LZW 圧縮スキーマが使用されます。

None
Tagged Image File Format (TIFF) イメージは圧縮されません。

Rle
RLE 圧縮スキーマが使用されます。

Zip
Zip 圧縮スキーマが使用されます。
引用ここまで

何も指定しないとDefaultになる

圧縮する画像は
イメージ 2
イメージ 3
イメージ 1
256x192ピクセルの24bitのbmp形式の
画像A
ファイルサイズは144KB(147510バイト)
256*192*24/8=147456

圧縮方式による見た目の違い
イメージ 4
Tiffは可逆圧縮なので見た目は元のBitmap画像とまったく同じはず
Ccitt3と4はファクシミリに使われている方式だから白黒2値になるみたい


ファイルサイズ変化
画像A
KB
元画像144
Ccitt314.2
Ccitt414.4
Default128
Lzw170
None144
Rle145
Zip132
圧縮なのにLzwとRleは元画像よりファイルサイズが増えている
一番効率がいいのはDefaultなので何も指定しないのがいいみたい

Defaultの説明では最適な圧縮方法を採りますってあるけどなんだろうって気になる
エクスプローラーのファイルのプロパティから見ると
イメージ 5
LZWってある
でもLzwを指定してできたファイルとはサイズが違いすぎる

ファイルのプロパティから見た圧縮方式
イメージ 6
指定した単語とは微妙に違ったり表示がなかったり
でもDefaultとLzwはどちらもLZWだねえ



TIFFファイルの圧縮フォーマット判別法
http://robaq.info/tiffcompression.xhtml
こちらを参考にして
バイナリエディタで確認してみた
イメージ 7
先頭2バイトが49 49なら
検索で03 01 03 00 01 00
イメージ 8
これで見つかった場所から12バイト分の後の4バイトの値が、5なら圧縮方式はLZWってことらしい
イメージ 9
DefaultもLzwも5
だけどLzwを指定したものは170KBってぜんぜん違うサイズだからなあ
LZWにも種類があるってことかしら
これ以上は難しそうなのでパス



別の画像を圧縮
イメージ 10
png画像が得意そうな画像B

プロパティでみると
イメージ 11
527x497ピクセルの32bitのbmp形式の画像B
ファイルサイズは990KB(1047730バイト)
527*497*32/8=1047676

ファイルサイズ変化
画像A画像B
KBKB
元画像144990
Ccitt3  14  31
Ccitt4  14  41
Default128107
Lzw170  93
None144769
Rle145575
Zip132  46

おお、スゴイ縮んだ
Defaultでも10分の1に圧縮できているし、一番効率が良かったZip形式では20分の1
可逆圧縮でこの圧縮率はスゴイ
png画像も同じくらい縮むから好きなんだけどTiff画像もいいねえ





今回参照したところ
TiffCompressOption 列挙型 (System.Windows.Media.Imaging)
https://msdn.microsoft.com/ja-jp/library/system.windows.media.imaging.tiffcompressoption(v=vs.110).aspx

TIFFファイルの圧縮フォーマット判別法
http://robaq.info/tiffcompression.xhtml

CGファイル概説 第5章 第2節 その2
http://www.snap-tck.com/room03/c02/cg/cg05_03.html

TIFFのフォーマット(その1) | JProgramer
http://jprogramer.com/libtiffcate/3188

TIFF Tag Reference, Baseline TIFF Tags
https://www.awaresystems.be/imaging/tiff/tifftags/baseline.html

TIFF - Wikipedia
https://en.wikipedia.org/wiki/TIFF
ありがとうございます




using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;

//TiffBitmapEncoder クラス(System.Windows.Media.Imaging)

namespace _20180115_TiffEncoder
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Title = this.ToString();
string filePath;
//filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Bgr32.bmp";
//filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_BlackWhite.png";
//filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Gray2.png";
//filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg";
//filePath = @"D:\ブログ用\チェック用2\NEC_1259_2018_01_14_午後わてん.jpg";
//filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Prgba64.png";
//filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Bgr24.bmp";
filePath = @"D:\ブログ用\WPF\20180113_画像読み込み07_02_.bmp";

BitmapSource source = GetBitmapSource10(filePath, true, 96, 96);

TiffTest2(source, TiffCompressOption.Ccitt3);
TiffTest2(source, TiffCompressOption.Ccitt4);
TiffTest2(source, TiffCompressOption.Default);
TiffTest2(source, TiffCompressOption.Lzw);
TiffTest2(source, TiffCompressOption.None);
TiffTest2(source, TiffCompressOption.Rle);
TiffTest2(source, TiffCompressOption.Zip);

}

private void TiffTest2(BitmapSource source, TiffCompressOption compressOption)
{
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
BitmapFrame frame = BitmapFrame.Create(source);
encoder.Frames.Add(frame);
encoder.Compression = compressOption;
string fileName = frame.Format.ToString() + "_" + encoder.Compression.ToString() + ".tiff";
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}

/// <summary>
/// ファイルパスとPixelFormatを正確にするか最適にするかを指定してBitmapSourceを取得、
/// dpiの変更は任意
/// </summary>
/// <param name="filePath">画像ファイルのフルパス</param>
/// <param name="accuratePixelFormat">Trueなら画像ファイルと同じ正確なPixelFormat、
/// Falseは今の環境で最適なものに変更される</param>
/// <param name="dpiX">無指定なら画像ファイルで指定されているdpiになる</param>
/// <param name="dpiY">無指定なら画像ファイルで指定されているdpiになる</param>
/// <returns></returns>
private BitmapSource GetBitmapSource10(
string filePath, bool accuratePixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
BitmapFrame bitmapFrame;
//PixelFormatを元の画像と同じにするかパソコンの環境に合わせるか
if (accuratePixelFormat)
{//画像と同じ
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
}
else
{//環境に最適なものに変更
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.None,
BitmapCacheOption.Default);
}

int w = bitmapFrame.PixelWidth;
int h = bitmapFrame.PixelHeight;
int stride = (w * bitmapFrame.Format.BitsPerPixel) / 8;
byte[] pixels = new byte[w * stride];
bitmapFrame.CopyPixels(pixels, stride, 0);
//dpi指定がなければ元の画像と同じdpiにする
if (dpiX == 0) { dpiX = bitmapFrame.DpiX; }
if (dpiY == 0) { dpiY = bitmapFrame.DpiY; }
//dpiを指定してBitmapSource作成
source = BitmapSource.Create(
w, h, dpiX, dpiY,
bitmapFrame.Format,
bitmapFrame.Palette, pixels, stride);
};

return source;
}
}
}

画像ファイルパスからBitmapSourceを作成
TiffBitmapEncoderのFrameにそれを入れて
Compressに圧縮形式を指定してからファイルに保存しているだけ
今回はXAML必要なかった、こういう時にコンソールアプリってのがあるんだろうなあ

画像ファイルパスからBitmapSource作成の
GetBitmapSource10は前回の記事からコピペ

TiffBitmapEncoderにFrame追加は
encoder.Frames.Add(取得したBitmapSource);
これだとエラーになったので
取得したBitmapSourceからBitmapFrame.Createで作成したものを追加で
BitmapFrame frame = BitmapFrame.Create(取得したBitmapSource);
encoder.Frames.Add(frame);
こうした

前回の記事
WPF、画像ファイルを開く方法まとめ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15325331.html






WPF、wdp画像ファイル作成時の画質設定とファイルサイズ、WmpBitmapEncoder

$
0
0

WmpBitmapEncoderを使ってwdp形式の画像を作成
作成時の画質の設定を変化させたときのファイルサイズと結果画像を比べてみた

WmpBitmapEncoder クラス (System.Windows.Media.Imaging)
https://msdn.microsoft.com/ja-jp/library/system.windows.media.imaging.wmpbitmapencoder(v=vs.110).aspx
ここ見るとたくさん設定ができるみたい

この中で画質を指定するのに関係ありそうなのが以下の3つ
UseCodecOptions
QualityLevel
ImageQualityLevel
Lossless


Lossless(ロスレス) = 無損失 = 可逆圧縮

UseCodecOptions
既定値はfalse、Trueにすると以下の3つが有効になる
QualityLevel、OverlapLevel、SubsamplingLevel

QualityLevel
既定値は10、1から255で指定,byte型、1で無損失
設定を有効にするにはUseCodecOptionsをTrueにしておく必要がある
1の無損失でもLosslessとはサイズが違う、少し小さくなった

ImageQualityLevel
既定値は0.9f、0から1で指定、float型、1で無損失
LosslessがTrueのときは無視される
UseCodecOptionsがTrueのときもQualityLevelが優先された
無損失だとLossless=trueの結果と同じになった

Lossless
既定値はfalse、Trueで無損失、可逆と不可逆の切り替え
UseCodecOptionsがTrueのときはQualityLevelが優先された
無損失だとImageQualityLevel=1.0fの結果と同じになった

優先順位、今回試した限りではこうだった
QualityLevel > Lossless > ImageQualityLevel
ただしQualityLevelUseCodecOptionsをtrueにしておく必要がある
規定値のfalseだとQualityLevelは無効になる



使う画像は
いつもの

WmpBitmapEncoderの既定値
イメージ 8
UseCodecOptionsはfalseなのでQualityLevelは無視されて
Losslessもfalseなので
ImageQualityLevelの0.9が適用される
これで作成すると
イメージ 9




QualityLevel
1から255までを32ずつ変化
イメージ 1
63でかなり劣化して、95以上は破綻している
255ではなぜか絵が出ているけどなんかへんだし
ファイルサイズも元画像より増えている
1から255まで指定できるけど実質1から100くらいかなあ


ImageQualityLevel
1.0から0.0まで0.1ずつ変化させた
イメージ 2
最低画質の0.0指定でも画像としてわかる
QualityLevelよりもこちらのほうが使いやすそう
WmpBitmapEncoderの既定値でこちらを使っているのも納得できるし
既定値の0.9も画質をファイルサイズのバランスがいいと思う



ロスレス(可逆圧縮)比較
イメージ 3

元画像144KB
Lossless=true  84KB
ImageQL  84KB
QualityLevel  78KB

QualityLevelが一番縮んで、ImageQualityLevelとLosslessTrueは同じサイズで
イメージ 4
中身も全く同じみたい

他の画像形式と比べてみると
TiffEncoder128KB(圧縮設定はDefault)
PngEncoder106KB
WmpBitmapEncoderのロスレスはかなり優秀なんじゃないかな
ロスレスでこれだけ差が出るのはすごいと思う
ほんとにロスレスなのかなあって疑ってしまう

まとめると
WmpBitmapEncoderの画質設定は
ImageQualityLevelを使えばいい
最低品質を指定しても画像が破綻することもなく
最高品質ならロスレスになる


その他の設定
イメージ 5
ImageDataDiscardLevelってのを変更したらモザイクになった、これ面白いねえ
サイズもめちゃくちゃ縮んで1/144とかモビルスーツがプラモデルになるレベル


イメージ 6
SubsamplingLevel
Level1から3までファイルサイズが違っているので何かしら違うみたい
Level0だとグレースケールになった



イメージ 7
OverlapLevel
これもファイルサイズ以外違いがわからん



イメージ 10
1ピクセルのしましまと色と透明のグラデーションを
組み合わせたpng形式の画像
pngなのでロスレス
イメージ 11
ファイルサイズはサイズは4KB
これをWmpのロスレスで作成したら

元画像  4KB
Lossless=true64KB
ImageQL64KB
QualityLevel77KB
残念なことに大幅に膨らんだ
ってことはWmpのロスレスは幾何学模様の画像は苦手で
写真画像が得意なのかも?
でも透明に関係しそうなalphaがついているプロパティがいくつかあるから
その辺を指定すると縮む?…としてもpng形式にしたほうが早いし
写真画像もjpeg形式のものしか持っていないからロスレスは無意味
思いついた、Wmpのロスレスは写真画像に文字や矢印なんかの図形を入れたものを保存する時には良さそう!

拡張子
イメージ 12
CodecInfoを見ると拡張子は.wdpか.jxrってある、どちらにするか迷う

HD Photo - Wikipedia
https://ja.wikipedia.org/wiki/HD_Photo
jxrのほうが新しいみたい




using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;

namespace _20180116_WmpBitmapEncoder
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Title = this.ToString();
string filePath;
filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Bgr24.bmp";
//半透明
//filePath = @"D:\ブログ用\テスト用画像\TransparentRect3.png";

BitmapSource source = GetBitmapSource10(filePath, true, 96, 96);

WmpImageQualityLevel(source);
WmpQualityLevel(source);
WmpOverlapLevel(source);
WmpSubsamplingLevel(source);
WmpImageDataDiscardLevel(source);
WmpLossless(source);
}



private void WmpImageQualityLevel(BitmapSource source)
{
string fileName = "";
for (int i = 0; i <= 10; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.ImageQualityLevel = (float)i / 10;
encoder.Frames.Add(BitmapFrame.Create(source));
fileName = nameof(WmpImageQualityLevel) + encoder.ImageQualityLevel.ToString("0.0") + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}

private void WmpQualityLevel(BitmapSource source)
{
string fileName = "";
for (int i = 255; i > -32; i -= 32)
{
var encoder = new WmpBitmapEncoder();
if (i < 1) { encoder.QualityLevel = 1; }
else { encoder.QualityLevel = (byte)i; }
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.UseCodecOptions = true;
fileName = nameof(WmpQualityLevel) + encoder.QualityLevel.ToString("000") + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}

private void WmpOverlapLevel(BitmapSource source)
{
string fileName = "";
for (byte i = 0; i <= 2; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.OverlapLevel = i;
encoder.UseCodecOptions = true;
fileName = nameof(WmpOverlapLevel) + encoder.OverlapLevel.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}

private void WmpSubsamplingLevel(BitmapSource source)
{
string fileName = "";
for (byte i = 0; i <= 3; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.SubsamplingLevel = i;
encoder.UseCodecOptions = true;
fileName = nameof(WmpSubsamplingLevel) + encoder.SubsamplingLevel.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}

private void WmpImageDataDiscardLevel(BitmapSource source)
{
string fileName = "";
for (byte i = 0; i <= 3; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.ImageDataDiscardLevel = i;
fileName = nameof(WmpImageDataDiscardLevel) + encoder.ImageDataDiscardLevel.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}

private void WmpLossless(BitmapSource source)
{
string fileName = "";
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Lossless = true;
fileName = nameof(WmpLossless) + "_" + encoder.Lossless.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}

}





/// <summary>
/// ファイルパスとPixelFormatを正確にするか最適にするかを指定してBitmapSourceを取得、
/// dpiの変更は任意
/// </summary>
/// <param name="filePath">画像ファイルのフルパス</param>
/// <param name="accuratePixelFormat">Trueなら画像ファイルと同じ正確なPixelFormat、
/// Falseは今の環境で最適なものに変更される</param>
/// <param name="dpiX">無指定なら画像ファイルで指定されているdpiになる</param>
/// <param name="dpiY">無指定なら画像ファイルで指定されているdpiになる</param>
/// <returns></returns>
private BitmapSource GetBitmapSource10(
string filePath, bool accuratePixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
BitmapFrame bitmapFrame;
//PixelFormatを元の画像と同じにするかパソコンの環境に合わせるか
if (accuratePixelFormat)
{//画像と同じ
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
}
else
{//環境に最適なものに変更
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.None,
BitmapCacheOption.Default);
}

int w = bitmapFrame.PixelWidth;
int h = bitmapFrame.PixelHeight;
int stride = (w * bitmapFrame.Format.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[w * stride];
bitmapFrame.CopyPixels(pixels, stride, 0);
//dpi指定がなければ元の画像と同じdpiにする
if (dpiX == 0) { dpiX = bitmapFrame.DpiX; }
if (dpiY == 0) { dpiY = bitmapFrame.DpiY; }
//dpiを指定してBitmapSource作成
source = BitmapSource.Create(
w, h, dpiX, dpiY,
bitmapFrame.Format,
bitmapFrame.Palette, pixels, stride);
};

return source;
}
}
}

実行すると
filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Bgr24.bmp";
とかで指定した画像ファイルを元に32個のwdpファイルが作成される


2018/01/17 20:19修正
int stride = (w * pixelFormat.BitsPerPixel) / 8;
↑を↓に書き直した
int stride = (w * pixelFormat.BitsPerPixel + 7) / 8;
これで8bpp以下で縦横ピクセルともに8の倍数じゃない画像でもエラーにならない



前回の記事
WPF、Tiff画像の圧縮形式とファイルサイズ、TiffBitmapEncoderのCompress ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15326818.html


WPF、カラー画像を白黒2値や2色の1bitビットマップ画像ファイルにしてみた

$
0
0

画像を白黒2値にして1bitのビットマップ形式のファイルに保存
PixelFormatはBlackWhite
ついでPixelFormatがIndexed1(2色パレット)の1bitビットマップファイルも作成


いつものこの画像を

イメージ 1
白黒2値に変換、PixelFormatはBlackWhite
ヤフーブログはbmp画像の投稿には対応していないから
上の画像はpngに変換したもの

ファイルのプロパティ
イメージ 2
ビットの深さ1(1bit)になっている
ファイルサイズも6KBと小さい
256*192*1/8=6144bit≒6KB


白黒以外の1bit
イメージ 3
PixelFormatはIndexed1
これは2色までのパレットを指定できるので
赤とオレンジのパレットで作成



デザイン画面
イメージ 4
確認用のImageをおいただけ


using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using System.Collections.Generic;


namespace _20180117_白黒2値画像作成
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Title = this.ToString();

string filePath;
filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Rgb24.bmp";

//PixelFormatを8bitグレースケールに変換してBitmapSourceを取得
BitmapSource source = GetBitmapSourceWithCangePixelFormat2(filePath, PixelFormats.Gray8, 96, 96);
//画像作成しきい値を指定できる
BlackWhite2(source, 128);
}



/// <summary>
/// PixelFormatがGray8(8bitグレースケール)のBitmapSourceを白黒2値に変換して
/// 1bitビットマップ画像ファイルに保存
/// </summary>
/// <param name="source">PixelFormatがGray8のBitmapSource</param>
/// <param name="threshold">しきい値</param>
private void BlackWhite2(BitmapSource source, byte threshold)
{
int w = source.PixelWidth;
int h = source.PixelHeight;
int stride = w;//1ピクセル行のbyte数を指定、Gray8は1ピクセル8bitなのでw * 8 / 8 = w
byte[] pixels = new byte[h * stride];
source.CopyPixels(pixels, stride, 0);
//しきい値で白黒つける
for (int i = 0; i < pixels.Length; ++i)
{
if (pixels[i] < threshold)
{
pixels[i] = 0;
}
else
{
pixels[i] = 255;
}
}

//BitmapSource作成、ここでPixelFormatをBlackWhiteにすると画像が崩れるのでそのままで作成
BitmapSource newBitmap = BitmapSource.Create(w, h, source.DpiX, source.DpiY, source.Format, null, pixels, stride);

//PixelFormatをBlackWhiteに変換
FormatConvertedBitmap convertedBitmap = new FormatConvertedBitmap(newBitmap, PixelFormats.BlackWhite, null, 0);

MyImage.Source = convertedBitmap;//表示

//ファイルに保存
//1ビットのbmpファイル作成
string fileName = nameof(BlackWhite2) + ".bmp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(convertedBitmap));
encoder.Save(fs);
}
}


/// <summary>
///  ファイルパスとPixelFormatを指定してBitmapSourceを取得、dpiの変更は任意
/// </summary>
/// <param name="filePath">画像ファイルのフルパス</param>
/// <param name="pixelFormat">PixelFormatsの中からどれかを指定</param>
/// <param name="dpiX">無指定なら画像ファイルで指定されているdpiになる</param>
/// <param name="dpiY">無指定なら画像ファイルで指定されているdpiになる</param>
/// <returns></returns>
private BitmapSource GetBitmapSourceWithCangePixelFormat2(
string filePath, PixelFormat pixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
var bf = BitmapFrame.Create(fs);
var convertedBitmap = new FormatConvertedBitmap(bf, pixelFormat, null, 0);
int w = convertedBitmap.PixelWidth;
int h = convertedBitmap.PixelHeight;
int stride = (w * pixelFormat.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[w * stride];
convertedBitmap.CopyPixels(pixels, stride, 0);
//dpi指定がなければ元の画像と同じdpiにする
if (dpiX == 0) { dpiX = bf.DpiX; }
if (dpiY == 0) { dpiY = bf.DpiY; }
//dpiを指定してBitmapSource作成
source = BitmapSource.Create(
w, h, dpiX, dpiY,
convertedBitmap.Format,
convertedBitmap.Palette, pixels, stride);
};
return source;
}
}
}


  1. 画像ファイルからBitmapSource作成
  2. PixelFormatをFormatConvertedBitmapでGray8に変換作成
  3. CopyPixelで色情報コピー
  4. 指定したしきい値で白と黒に分ける
  5. PixelFormatGray8のままBitmapSource作成
  6. FormatConvertedBitmapでPixelFormatをGray8からBlackWhiteに変換して作成
  7. BitmapFrameを作成してファイルに保存

BitmapSourceを5回も作っている?CopyPixelもカウントしたらさらに+1
ムダが多い気がするけど、この方法が分かりやすかった
4番でラクをするために2番で8bitグレースケールにしている、グレースケール変換はWPFにおまかせしているので、ここでもラクをしている
GetBitmapSourceWithCangePixelFormat2
これもこの前の記事のコピペ
楽ちん


2番の
PixelFormatをFormatConvertedBitmapでGray8に変換作成
イメージ 14
この状態で読み込まれて
ここから白黒2値にするんだけど

これをGray8じゃなくて直接BlackWhiteに変換作成すると
イメージ 13
ディザリングされた白黒2値になってしまう
今回はディザリングなしの白黒2値が目的だからねえ



5番の
PixelFormatGray8のままBitmapSource作成の
BitmapSource newBitmap = BitmapSource.Create(w, h, source.DpiX, source.DpiY, source.Format, null, pixels, stride);
ここをBlackWhiteで作成すると
BitmapSource newBitmap = BitmapSource.Create(w, h, source.DpiX, source.DpiY, PixelFormats.BlackWhite, null, pixels, stride);
イメージ 12
崩壊する
strideとPixelFormatのbppが噛み合っていないからかなあ





白黒2値じゃなくて他の色2色にするときは

//PixelFormatをBlackWhiteに変換
FormatConvertedBitmap convertedBitmap = new FormatConvertedBitmap(newBitmap, PixelFormats.BlackWhite, null, 0);

↑をコメントアウトして
↓を書き加える

//パレット指定してIndexed1
List<Color> list = new List<Color> { Colors.Red, Colors.Orange };
BitmapPalette palette = new BitmapPalette(list);
FormatConvertedBitmap convertedBitmap = new FormatConvertedBitmap(newBitmap, PixelFormats.Indexed1, palette, 0);


イメージ 5

List<Color> list = new List<Color> { Colors.Red, Colors.Beige };
BitmapPalette palette = new BitmapPalette(list);

ここでパレットに赤と(オレンジは目が痛かったので)ベージュにしている
結果は
イメージ 6
List<Color> list = new List<Color> { Colors.Red, Colors.Beige };
これを
List<Color> list = new List<Color> { Colors.BeigeColors.Red };
にしたら色が入れ替わるかな?
イメージ 7
変化なし…
どうやら指定した2色を白黒どちらに近いか判定して
元の白黒の配置に近くなるようにしているみたい





しきい値
イメージ 9
100
これだとプランターがあるのがわかる

イメージ 10
70
これは白すぎた

イメージ 11
140
プランターは消え去ったけど、いちごが見やすくなった

イメージ 8
List<Color> list = new List<Color> { Colors.DarkBlue, Colors.DarkMagenta };




関連記事
WPF、画像ファイルを開く方法まとめ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15325331.html









カラー画像を1bpp(1bit)白黒画像に変換して保存するアプリ作ってみた、しきい値は手動設定

$
0
0

画像を白黒2値に変換して保存するアプリ
イメージ 1
しきい値を変更しているところ
平均輝度のボタンで画像の平均輝度をしきい値に設定



画像を開くときはファイルをドラッグアンドドロップ
対応するのは普通の画像ファイルで
画像形式はbmp、jpeg、png、tiff、gif、wdpなどの8bpp以上の画像
イメージ 2
こういうカラー画像もグレースケールで表示される


変換した画像の保存形式
イメージ 3
開ける画像形式とだいたい同じ


保存画像のbppを1, 8, 32から指定できる
それぞれのPixelFormatは
1bppがBlackWhite
8bppがGray8
32bppがBgr32
効率がいいのは1bpp
8と32はムダにファイルサイズが大きくなるけど一般的な画像形式


bmp
1bppを指定した場合、なぜかPixelFormatがIndexed1になってしまうけど、同じ1bppだから問題ない?

png
指定通りに保存される
gif
どれを指定しても8bppになる

jpeg
1bppと8bppが8bitグレースケール、32bppは24bppカラーになる
jpegの画質指定は75
2値画像でjpegの意味は薄いからおまけ程度

wdp
問題ない
ロスレス(可逆圧縮)設定で保存している

tiff
pngやwdp同様、指定通りに保存される

tiff形式で保存するときの圧縮形式の選択
イメージ 4
選択できるようにしてみた

保存形式ファイルサイズ(KB)
tiff(Ccitt4)3.1
png3.7
gif3.9
tiff(Default)4.2
wdp5.7
bmp6.1
jpeg30.8

上の画像を1bppで保存した時に
一番効率が良かったのはtiff画像をCcitt4って言う圧縮方式だった
jpegは不可逆圧縮だからねえ…白黒はっきりした画像には弱い
wdpの1bpp保存はWmpBitmapEncoderの初期設定だと画像が崩壊していたけど、ロスレス指定で正常に保存できた

イメージ 5
左画像はロスレス指定、ImageQualityLevel = 1.0f;
右画像は初期値でImageQualityLevel = 0.9f;
1bpp画像に不可逆圧縮は意味が薄いってことなんだろうねえ





しきい値を決める方法に大津の2値化ってのがあるんだけど、難しくてできなかった…かわりに画像の平均輝度をしきい値にするものだけ付けてみたのが今回のアプリ
目的は1bppのBitmap画像を作ってみたかったとか、画像の減色やディザリングを試してみたかったはずなんだけど、よくわかんなくなってきたなあ
白黒2値画像はまだ続く













Viewing all 420 articles
Browse latest View live